mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Merge m-c to fx-team.
This commit is contained in:
commit
7e8c73f3e6
@ -918,10 +918,18 @@ var Input = {
|
||||
},
|
||||
|
||||
moveToPoint: function moveToPoint(aRule, aX, aY) {
|
||||
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
|
||||
mm.sendAsyncMessage('AccessFu:MoveToPoint', {rule: aRule,
|
||||
x: aX, y: aY,
|
||||
origin: 'top'});
|
||||
// XXX: Bug 1013408 - There is no alignment between the chrome window's
|
||||
// viewport size and the content viewport size in Android. This makes
|
||||
// sending mouse events beyond its bounds impossible.
|
||||
if (Utils.MozBuildApp === 'mobile/android') {
|
||||
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
|
||||
mm.sendAsyncMessage('AccessFu:MoveToPoint',
|
||||
{rule: aRule, x: aX, y: aY, origin: 'top'});
|
||||
} else {
|
||||
let win = Utils.win;
|
||||
Utils.winUtils.sendMouseEvent('mousemove',
|
||||
aX - win.mozInnerScreenX, aY - win.mozInnerScreenY, 0, 0, 0);
|
||||
}
|
||||
},
|
||||
|
||||
moveCursor: function moveCursor(aAction, aRule, aInputType) {
|
||||
@ -999,11 +1007,9 @@ var Input = {
|
||||
let page = aDetails.page;
|
||||
let p = AccessFu.adjustContentBounds(aDetails.bounds, Utils.CurrentBrowser,
|
||||
true, true).center();
|
||||
let wu = Utils.win.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils);
|
||||
wu.sendWheelEvent(p.x, p.y,
|
||||
horizontal ? page : 0, horizontal ? 0 : page, 0,
|
||||
Utils.win.WheelEvent.DOM_DELTA_PAGE, 0, 0, 0, 0);
|
||||
Utils.winUtils.sendWheelEvent(p.x, p.y,
|
||||
horizontal ? page : 0, horizontal ? 0 : page, 0,
|
||||
Utils.win.WheelEvent.DOM_DELTA_PAGE, 0, 0, 0, 0);
|
||||
},
|
||||
|
||||
get keyMap() {
|
||||
|
@ -45,6 +45,7 @@ this.ContentControl.prototype = {
|
||||
for (let message of this.messagesOfInterest) {
|
||||
cs.addMessageListener(message, this);
|
||||
}
|
||||
cs.addEventListener('mousemove', this);
|
||||
},
|
||||
|
||||
stop: function cc_stop() {
|
||||
@ -52,6 +53,7 @@ this.ContentControl.prototype = {
|
||||
for (let message of this.messagesOfInterest) {
|
||||
cs.removeMessageListener(message, this);
|
||||
}
|
||||
cs.removeEventListener('mousemove', this);
|
||||
},
|
||||
|
||||
get document() {
|
||||
@ -124,18 +126,22 @@ this.ContentControl.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function cc_handleEvent(aEvent) {
|
||||
if (aEvent.type === 'mousemove') {
|
||||
this.handleMoveToPoint(
|
||||
{ json: { x: aEvent.screenX, y: aEvent.screenY, rule: 'Simple' } });
|
||||
}
|
||||
if (!Utils.getMessageManager(aEvent.target)) {
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
handleMoveToPoint: function cc_handleMoveToPoint(aMessage) {
|
||||
let [x, y] = [aMessage.json.x, aMessage.json.y];
|
||||
let rule = TraversalRules[aMessage.json.rule];
|
||||
let vc = this.vc;
|
||||
let win = this.window;
|
||||
|
||||
let dpr = win.devicePixelRatio;
|
||||
let dpr = this.window.devicePixelRatio;
|
||||
this.vc.moveToPoint(rule, x * dpr, y * dpr, true);
|
||||
|
||||
let delta = Utils.isContentProcess ?
|
||||
{ x: x - win.mozInnerScreenX, y: y - win.mozInnerScreenY } : {};
|
||||
this.sendToChild(vc, aMessage, delta);
|
||||
},
|
||||
|
||||
handleClearCursor: function cc_handleClearCursor(aMessage) {
|
||||
|
@ -110,36 +110,14 @@ let PointerRelay = { // jshint ignore:line
|
||||
}
|
||||
},
|
||||
|
||||
_suppressPointerMove: function PointerRelay__suppressPointerMove(aChangedTouches) {
|
||||
if (!this.lastPointerMove) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < aChangedTouches.length; ++i) {
|
||||
let touch = aChangedTouches[i];
|
||||
let lastTouch;
|
||||
try {
|
||||
lastTouch = this.lastPointerMove.identifiedTouch ?
|
||||
this.lastPointerMove.identifiedTouch(touch.identifier) :
|
||||
this.lastPointerMove[i];
|
||||
} catch (x) {
|
||||
// Sometimes touch object can't be accessed after page navigation.
|
||||
}
|
||||
if (!lastTouch || lastTouch.target !== touch.target ||
|
||||
Math.hypot(touch.screenX - lastTouch.screenX, touch.screenY -
|
||||
lastTouch.screenY) / Utils.dpi >= GestureSettings.travelThreshold) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
handleEvent: function PointerRelay_handleEvent(aEvent) {
|
||||
// Don't bother with chrome mouse events.
|
||||
if (Utils.MozBuildApp === 'browser' &&
|
||||
aEvent.view.top instanceof Ci.nsIDOMChromeWindow) {
|
||||
return;
|
||||
}
|
||||
if (aEvent.mozInputSource === Ci.nsIDOMMouseEvent.MOZ_SOURCE_UNKNOWN) {
|
||||
if (aEvent.mozInputSource === Ci.nsIDOMMouseEvent.MOZ_SOURCE_UNKNOWN ||
|
||||
aEvent.isSynthesized) {
|
||||
// Ignore events that are scripted or clicks from the a11y API.
|
||||
return;
|
||||
}
|
||||
@ -164,13 +142,6 @@ let PointerRelay = { // jshint ignore:line
|
||||
return;
|
||||
}
|
||||
let pointerType = this._eventMap[type];
|
||||
if (pointerType === 'pointermove') {
|
||||
if (this._suppressPointerMove(changedTouches)) {
|
||||
// Do not fire pointermove more than every POINTERMOVE_THROTTLE.
|
||||
return;
|
||||
}
|
||||
this.lastPointerMove = changedTouches;
|
||||
}
|
||||
this.onPointerEvent({
|
||||
type: pointerType,
|
||||
points: Array.prototype.map.call(changedTouches,
|
||||
|
@ -113,6 +113,11 @@ var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
|
||||
|
||||
function isFlatSubtree(acc) {
|
||||
for (let child = acc.firstChild; child; child = child.nextSibling) {
|
||||
// text leafs inherit the actionCount of any ancestor that has a click
|
||||
// listener.
|
||||
if ([Roles.TEXT_LEAF, Roles.STATICTEXT].indexOf(child.role) >= 0) {
|
||||
continue;
|
||||
}
|
||||
if (child.childCount > 0 || child.actionCount > 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -54,6 +54,15 @@ this.Utils = {
|
||||
return this._win.get();
|
||||
},
|
||||
|
||||
get winUtils() {
|
||||
let win = this.win;
|
||||
if (!win) {
|
||||
return null;
|
||||
}
|
||||
return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(
|
||||
Ci.nsIDOMWindowUtils);
|
||||
},
|
||||
|
||||
get AccRetrieval() {
|
||||
if (!this._AccRetrieval) {
|
||||
this._AccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
|
||||
@ -181,7 +190,6 @@ this.Utils = {
|
||||
return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
|
||||
frameLoader.messageManager;
|
||||
} catch (x) {
|
||||
Logger.logException(x);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
@ -252,8 +260,7 @@ this.Utils = {
|
||||
*/
|
||||
get dpi() {
|
||||
delete this.dpi;
|
||||
this.dpi = this.win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(
|
||||
Ci.nsIDOMWindowUtils).displayDPI;
|
||||
this.dpi = this.winUtils.displayDPI;
|
||||
return this.dpi;
|
||||
},
|
||||
|
||||
|
@ -60,7 +60,7 @@
|
||||
<dd>A esoteric weapon wielded by only the most formidable warriors,
|
||||
for its unrelenting strict power is unfathomable.</dd>
|
||||
</dl>
|
||||
<ul id="list-2">
|
||||
<ul id="list-2" onclick="alert('hi');">
|
||||
<li id="listitem-2-1">Lists of Programming Languages</li>
|
||||
<li id="listitem-2-2">Lisp
|
||||
<ol id="list-3">
|
||||
|
@ -29,11 +29,6 @@ component {1a94c87a-5ece-4d11-91e1-d29c29f21b28} ProcessGlobal.js
|
||||
contract @mozilla.org/b2g-process-global;1 {1a94c87a-5ece-4d11-91e1-d29c29f21b28}
|
||||
category app-startup ProcessGlobal service,@mozilla.org/b2g-process-global;1
|
||||
|
||||
# ContentHandler.js
|
||||
component {d18d0216-d50c-11e1-ba54-efb18d0ef0ac} ContentHandler.js
|
||||
contract @mozilla.org/b2g/activities-content-handler;1 {d18d0216-d50c-11e1-ba54-efb18d0ef0ac}
|
||||
category app-startup ContentHandler service,@mozilla.org/b2g/activities-content-handler;1
|
||||
|
||||
# PaymentGlue.js
|
||||
component {8b83eabc-7929-47f4-8b48-4dea8d887e4b} PaymentGlue.js
|
||||
contract @mozilla.org/payment/ui-glue;1 {8b83eabc-7929-47f4-8b48-4dea8d887e4b}
|
||||
|
@ -1,145 +0,0 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
|
||||
return Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageSender);
|
||||
});
|
||||
|
||||
function debug(aMsg) {
|
||||
//dump("--*-- ContentHandler: " + aMsg + "\n");
|
||||
}
|
||||
|
||||
const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
|
||||
|
||||
let ActivityContentFactory = {
|
||||
createInstance: function createInstance(outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return new ActivityContentHandler().QueryInterface(iid);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
|
||||
}
|
||||
|
||||
function ActivityContentHandler() {
|
||||
}
|
||||
|
||||
ActivityContentHandler.prototype = {
|
||||
handleContent: function handleContent(aMimetype, aContext, aRequest) {
|
||||
if (!(aRequest instanceof Ci.nsIChannel))
|
||||
throw NS_ERROR_WONT_HANDLE_CONTENT;
|
||||
|
||||
let detail = {
|
||||
"type": aMimetype,
|
||||
"url": aRequest.URI.spec
|
||||
};
|
||||
cpmm.sendAsyncMessage("content-handler", detail);
|
||||
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler])
|
||||
}
|
||||
|
||||
function ContentHandler() {
|
||||
this.classIdMap = {};
|
||||
}
|
||||
|
||||
ContentHandler.prototype = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "app-startup") {
|
||||
// We only want to register these from content processes.
|
||||
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
|
||||
if (appInfo.getService(Ci.nsIXULRuntime)
|
||||
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cpmm.addMessageListener("Activities:RegisterContentTypes", this);
|
||||
cpmm.addMessageListener("Activities:UnregisterContentTypes", this);
|
||||
cpmm.sendAsyncMessage("Activities:GetContentTypes", { });
|
||||
},
|
||||
|
||||
/**
|
||||
* Do the component registration for a content type.
|
||||
* We only need to register one component per content type, even if several
|
||||
* apps provide it, so we keep track of the number of providers for each
|
||||
* content type.
|
||||
*/
|
||||
registerContentHandler: function registerContentHandler(aContentType) {
|
||||
debug("Registering " + aContentType);
|
||||
|
||||
// We already have a provider for this content type, just increase the
|
||||
// tracking count.
|
||||
if (this.classIdMap[aContentType]) {
|
||||
this.classIdMap[aContentType].count++;
|
||||
return;
|
||||
}
|
||||
|
||||
let contractID = "@mozilla.org/uriloader/content-handler;1?type=" +
|
||||
aContentType;
|
||||
let uuidGen = Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator);
|
||||
let id = Components.ID(uuidGen.generateUUID().toString());
|
||||
this.classIdMap[aContentType] = { count: 1, id: id };
|
||||
let cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
cr.registerFactory(Components.ID(id), "Activity Content Handler", contractID,
|
||||
ActivityContentFactory);
|
||||
},
|
||||
|
||||
/**
|
||||
* Do the component unregistration for a content type.
|
||||
*/
|
||||
unregisterContentHandler: function registerContentHandler(aContentType) {
|
||||
debug("Unregistering " + aContentType);
|
||||
|
||||
let record = this.classIdMap[aContentType];
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail out if we still have providers left for this content type.
|
||||
if (--record.count > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let contractID = "@mozilla.org/uriloader/content-handler;1?type=" +
|
||||
aContentType;
|
||||
let cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
cr.unregisterFactory(record.id, ActivityContentFactory);
|
||||
delete this.classIdMap[aContentType]
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let data = aMessage.data;
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "Activities:RegisterContentTypes":
|
||||
data.contentTypes.forEach(this.registerContentHandler, this);
|
||||
break;
|
||||
case "Activities:UnregisterContentTypes":
|
||||
data.contentTypes.forEach(this.unregisterContentHandler, this);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
classID: Components.ID("{d18d0216-d50c-11e1-ba54-efb18d0ef0ac}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentHandler]);
|
@ -10,7 +10,6 @@ EXTRA_COMPONENTS += [
|
||||
'ActivitiesGlue.js',
|
||||
'AlertsService.js',
|
||||
'B2GAboutRedirector.js',
|
||||
'ContentHandler.js',
|
||||
'ContentPermissionPrompt.js',
|
||||
'FilePicker.js',
|
||||
'HelperAppDialog.js',
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="65fba428f8d76336b33ddd9e15900357953600ba">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "2f15fe97023bf41e29660a2d427bd43a3ff181b0",
|
||||
"revision": "9b29b2b76fa9038d3162261c174a92dd5ef704d2",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
|
@ -6,8 +6,8 @@
|
||||
"filename": "setup.sh"
|
||||
},
|
||||
{
|
||||
"size": 165464,
|
||||
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
"filename": "setup.sh"
|
||||
},
|
||||
{
|
||||
"size": 165464,
|
||||
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -15,8 +15,8 @@
|
||||
"filename": "clang.tar.bz2"
|
||||
},
|
||||
{
|
||||
"size": 165464,
|
||||
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c462d9183d294a2d8ecc472f593ea8cfa15bc9de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7c55cc2baabc2d66d512768e79b9cbc67bb83040"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="996b5c6a2fd2b8a0124c0eab80eb72a4daece7bc"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -32,13 +32,14 @@ ul {
|
||||
max-width: 512px;
|
||||
}
|
||||
|
||||
#errorTitle {
|
||||
#errorTitleText {
|
||||
background: url("info.svg") left 0 no-repeat;
|
||||
-moz-margin-start: -5em;
|
||||
-moz-padding-start: 5em;
|
||||
background-size: 1.2em;
|
||||
-moz-margin-start: -2em;
|
||||
-moz-padding-start: 2em;
|
||||
}
|
||||
|
||||
#errorTitle:-moz-dir(rtl) {
|
||||
#errorTitleText:-moz-dir(rtl) {
|
||||
background-position: right 0;
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
"filename": "gcc.tar.xz"
|
||||
},
|
||||
{
|
||||
"size": 165464,
|
||||
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -12,8 +12,8 @@
|
||||
"filename": "gcc.tar.xz"
|
||||
},
|
||||
{
|
||||
"size": 165464,
|
||||
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -15,8 +15,8 @@
|
||||
"filename": "clang.tar.bz2"
|
||||
},
|
||||
{
|
||||
"size": 165464,
|
||||
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -12,8 +12,8 @@
|
||||
"filename": "setup.sh"
|
||||
},
|
||||
{
|
||||
"size": 165464,
|
||||
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -474,6 +474,13 @@ ThreadState.prototype = {
|
||||
* Update the UI after a thread state change.
|
||||
*/
|
||||
_update: function(aEvent) {
|
||||
// Ignore "interrupted" events, which are generated by the slow script
|
||||
// dialog and internal events such as setting breakpoints, to avoid UI
|
||||
// flicker.
|
||||
if (aEvent == "interrupted") {
|
||||
return;
|
||||
}
|
||||
|
||||
DebuggerView.Toolbar.toggleResumeButtonState(this.activeThread.state);
|
||||
|
||||
if (gTarget && (aEvent == "paused" || aEvent == "resumed")) {
|
||||
@ -580,6 +587,11 @@ StackFrames.prototype = {
|
||||
this._currentReturnedValue = aPacket.why.frameFinished.return;
|
||||
}
|
||||
break;
|
||||
// If paused by an explicit interrupt, which are generated by the slow
|
||||
// script dialog and internal events such as setting breakpoints, ignore
|
||||
// the event to avoid UI flicker.
|
||||
case "interrupted":
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
|
||||
|
@ -78,6 +78,7 @@ support-files =
|
||||
doc_script-switching-01.html
|
||||
doc_script-switching-02.html
|
||||
doc_step-out.html
|
||||
doc_terminate-on-tab-close.html
|
||||
doc_tracing-01.html
|
||||
doc_watch-expressions.html
|
||||
doc_watch-expression-button.html
|
||||
@ -237,6 +238,7 @@ skip-if = true # Bug 933950 (leaky test)
|
||||
[browser_dbg_step-out.js]
|
||||
[browser_dbg_tabactor-01.js]
|
||||
[browser_dbg_tabactor-02.js]
|
||||
[browser_dbg_terminate-on-tab-close.js]
|
||||
[browser_dbg_tracing-01.js]
|
||||
[browser_dbg_tracing-02.js]
|
||||
[browser_dbg_tracing-03.js]
|
||||
|
@ -0,0 +1,38 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that debuggee scripts are terminated on tab closure.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_terminate-on-tab-close.html";
|
||||
|
||||
let gTab, gDebuggee, gDebugger, gPanel;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
|
||||
testTerminate();
|
||||
});
|
||||
}
|
||||
|
||||
function testTerminate() {
|
||||
gDebugger.gThreadClient.addOneTimeListener("paused", () => {
|
||||
resumeDebuggerThenCloseAndFinish(gPanel).then(function () {
|
||||
ok(true, "should not throw after this point");
|
||||
});
|
||||
});
|
||||
|
||||
gDebuggee.debuggerThenThrow();
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
});
|
@ -0,0 +1,20 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
function debuggerThenThrow() {
|
||||
debugger;
|
||||
throw "unreachable";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -580,6 +580,79 @@ let gDevToolsBrowser = {
|
||||
mainKeyset.parentNode.insertBefore(devtoolsKeyset, mainKeyset);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hook the JS debugger tool to the "Debug Script" button of the slow script
|
||||
* dialog.
|
||||
*/
|
||||
setSlowScriptDebugHandler: function DT_setSlowScriptDebugHandler() {
|
||||
let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
|
||||
.getService(Ci.nsISlowScriptDebug);
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
||||
|
||||
debugService.activationHandler = function(aWindow) {
|
||||
let chromeWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
let target = devtools.TargetFactory.forTab(chromeWindow.gBrowser.selectedTab);
|
||||
|
||||
let setupFinished = false;
|
||||
gDevTools.showToolbox(target, "jsdebugger").then(toolbox => {
|
||||
let threadClient = toolbox.getCurrentPanel().panelWin.gThreadClient;
|
||||
|
||||
// Break in place, which means resuming the debuggee thread and pausing
|
||||
// right before the next step happens.
|
||||
switch (threadClient.state) {
|
||||
case "paused":
|
||||
// When the debugger is already paused.
|
||||
threadClient.breakOnNext();
|
||||
setupFinished = true;
|
||||
break;
|
||||
case "attached":
|
||||
// When the debugger is already open.
|
||||
threadClient.interrupt(() => {
|
||||
threadClient.breakOnNext();
|
||||
setupFinished = true;
|
||||
});
|
||||
break;
|
||||
case "resuming":
|
||||
// The debugger is newly opened.
|
||||
threadClient.addOneTimeListener("resumed", () => {
|
||||
threadClient.interrupt(() => {
|
||||
threadClient.breakOnNext();
|
||||
setupFinished = true;
|
||||
});
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw Error("invalid thread client state in slow script debug handler: " +
|
||||
threadClient.state);
|
||||
}
|
||||
});
|
||||
|
||||
// Don't return from the interrupt handler until the debugger is brought
|
||||
// up; no reason to continue executing the slow script.
|
||||
let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
utils.enterModalState();
|
||||
while (!setupFinished) {
|
||||
tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
utils.leaveModalState();
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Unset the slow script debug handler.
|
||||
*/
|
||||
unsetSlowScriptDebugHandler: function DT_unsetSlowScriptDebugHandler() {
|
||||
let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
|
||||
.getService(Ci.nsISlowScriptDebug);
|
||||
debugService.activationHandler = undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Detect the presence of a Firebug.
|
||||
@ -669,6 +742,10 @@ let gDevToolsBrowser = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toolDefinition.id === "jsdebugger") {
|
||||
gDevToolsBrowser.setSlowScriptDebugHandler();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -844,6 +921,10 @@ let gDevToolsBrowser = {
|
||||
for (let win of gDevToolsBrowser._trackedBrowserWindows) {
|
||||
gDevToolsBrowser._removeToolFromMenu(toolId, win.document);
|
||||
}
|
||||
|
||||
if (toolId === "jsdebugger") {
|
||||
gDevToolsBrowser.unsetSlowScriptDebugHandler();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -531,6 +531,9 @@
|
||||
@BINPATH@/components/Push.manifest
|
||||
@BINPATH@/components/PushServiceLauncher.js
|
||||
|
||||
@BINPATH@/components/SlowScriptDebug.manifest
|
||||
@BINPATH@/components/SlowScriptDebug.js
|
||||
|
||||
#ifndef RELEASE_BUILD
|
||||
@BINPATH@/components/InterAppComm.manifest
|
||||
@BINPATH@/components/InterAppCommService.js
|
||||
|
@ -20,3 +20,5 @@ GENERATED_SOURCES += [
|
||||
DEFINES['ELFHACK_BUILD'] = True
|
||||
|
||||
NO_PGO = True
|
||||
|
||||
NO_VISIBILITY_FLAGS = True
|
||||
|
11
config/gcc_hidden_dso_handle.h
Normal file
11
config/gcc_hidden_dso_handle.h
Normal file
@ -0,0 +1,11 @@
|
||||
/* 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/. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
void *__dso_handle;
|
||||
|
||||
/* Begin all files as hidden visibility */
|
||||
#pragma GCC visibility push(hidden)
|
@ -407,9 +407,6 @@ DriverServices.h
|
||||
DriverSynchronization.h
|
||||
DropInPanel.h
|
||||
dvidef.h
|
||||
#ifdef ANDROID
|
||||
EffectApi.h
|
||||
#endif
|
||||
elf.h
|
||||
endian.h
|
||||
Entry.h
|
||||
@ -516,6 +513,8 @@ gtk/gtkprinter.h
|
||||
gtk/gtkprintjob.h
|
||||
gtk/gtkprintunixdialog.h
|
||||
#ifdef ANDROID
|
||||
gui/BufferQueue.h
|
||||
gui/ConsumerBase.h
|
||||
gui/GraphicBufferAlloc.h
|
||||
gui/IConsumerListener.h
|
||||
gui/IGraphicBufferAlloc.h
|
||||
@ -542,14 +541,6 @@ hlink.h
|
||||
HTTPBase.h
|
||||
#endif
|
||||
ia64/sys/inline.h
|
||||
#ifdef ANDROID
|
||||
IAudioFlingerClient.h
|
||||
IAudioFlinger.h
|
||||
IAudioRecord.h
|
||||
IAudioTrack.h
|
||||
IEffect.h
|
||||
IEffectClient.h
|
||||
#endif
|
||||
Icons.h
|
||||
iconv.h
|
||||
ieeefp.h
|
||||
@ -911,6 +902,7 @@ rpc/types.h
|
||||
sane/sane.h
|
||||
sane/sanei.h
|
||||
sane/saneopts.h
|
||||
sched.h
|
||||
Scrap.h
|
||||
Screen.h
|
||||
Script.h
|
||||
|
18
configure.in
18
configure.in
@ -2550,17 +2550,19 @@ dnl ===============================================================
|
||||
if test "$GNU_CC" -a "$OS_TARGET" != WINNT; then
|
||||
AC_DEFINE(HAVE_VISIBILITY_HIDDEN_ATTRIBUTE)
|
||||
AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE)
|
||||
if test -n "$gonkdir"; then
|
||||
visibility_target=Gonk
|
||||
else
|
||||
visibility_target=$OS_TARGET
|
||||
fi
|
||||
case "$visibility_target" in
|
||||
Darwin|Gonk)
|
||||
case "$OS_TARGET" in
|
||||
Darwin)
|
||||
VISIBILITY_FLAGS='-fvisibility=hidden'
|
||||
;;
|
||||
*)
|
||||
VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden.h'
|
||||
case $GCC_VERSION in
|
||||
4.4*)
|
||||
VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden_dso_handle.h'
|
||||
;;
|
||||
*)
|
||||
VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden.h'
|
||||
;;
|
||||
esac
|
||||
WRAP_SYSTEM_INCLUDES=1
|
||||
;;
|
||||
esac
|
||||
|
@ -1334,6 +1334,12 @@ public:
|
||||
*/
|
||||
static nsIPrincipal* GetSystemPrincipal();
|
||||
|
||||
/**
|
||||
* Gets the null subject principal singleton. This is only useful for
|
||||
* assertions.
|
||||
*/
|
||||
static nsIPrincipal* GetNullSubjectPrincipal() { return sNullSubjectPrincipal; }
|
||||
|
||||
/**
|
||||
* *aResourcePrincipal is a principal describing who may access the contents
|
||||
* of a resource. The resource can only be consumed by a principal that
|
||||
@ -2169,6 +2175,7 @@ private:
|
||||
|
||||
static nsIScriptSecurityManager *sSecurityManager;
|
||||
static nsIPrincipal *sSystemPrincipal;
|
||||
static nsIPrincipal *sNullSubjectPrincipal;
|
||||
|
||||
static nsIParserService *sParserService;
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nsIDOMHTMLDocument.h"
|
||||
#include "nsIDOMHTMLElement.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIObjectInputStream.h"
|
||||
#include "nsIObjectOutputStream.h"
|
||||
@ -26,6 +27,7 @@
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
#include "nsString.h"
|
||||
#include "prlog.h"
|
||||
@ -45,6 +47,47 @@ GetCspContextLog()
|
||||
|
||||
#define CSPCONTEXTLOG(args) PR_LOG(GetCspContextLog(), 4, args)
|
||||
|
||||
static const uint32_t CSP_CACHE_URI_CUTOFF_SIZE = 512;
|
||||
|
||||
/**
|
||||
* Creates a key for use in the ShouldLoad cache.
|
||||
* Looks like: <uri>!<nsIContentPolicy::LOAD_TYPE>
|
||||
*/
|
||||
nsresult
|
||||
CreateCacheKey_Internal(nsIURI* aContentLocation,
|
||||
nsContentPolicyType aContentType,
|
||||
nsACString& outCacheKey)
|
||||
{
|
||||
if (!aContentLocation) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool isDataScheme = false;
|
||||
nsresult rv = aContentLocation->SchemeIs("data", &isDataScheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
outCacheKey.Truncate();
|
||||
if (aContentType != nsIContentPolicy::TYPE_SCRIPT && isDataScheme) {
|
||||
// For non-script data: URI, use ("data:", aContentType) as the cache key.
|
||||
outCacheKey.Append(NS_LITERAL_CSTRING("data:"));
|
||||
outCacheKey.AppendInt(aContentType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString spec;
|
||||
rv = aContentLocation->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Don't cache for a URI longer than the cutoff size.
|
||||
if (spec.Length() <= CSP_CACHE_URI_CUTOFF_SIZE) {
|
||||
outCacheKey.Append(spec);
|
||||
outCacheKey.Append(NS_LITERAL_CSTRING("!"));
|
||||
outCacheKey.AppendInt(aContentType);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* ===== nsIContentSecurityPolicy impl ====== */
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -74,6 +117,16 @@ nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
|
||||
// * Content Type is not whitelisted (CSP Reports, TYPE_DOCUMENT, etc).
|
||||
// * Fast Path for Apps
|
||||
|
||||
nsAutoCString cacheKey;
|
||||
rv = CreateCacheKey_Internal(aContentLocation, aContentType, cacheKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool isCached = mShouldLoadCache.Get(cacheKey, outDecision);
|
||||
if (isCached && cacheKey.Length() > 0) {
|
||||
// this is cached, use the cached value.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Default decision, CSP can revise it if there's a policy to enforce
|
||||
*outDecision = nsIContentPolicy::ACCEPT;
|
||||
|
||||
@ -137,6 +190,11 @@ nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
|
||||
// * Console error reporting, bug 994322
|
||||
}
|
||||
}
|
||||
// Done looping, cache any relevant result
|
||||
if (cacheKey.Length() > 0 && !isPreload) {
|
||||
mShouldLoadCache.Put(cacheKey, *outDecision);
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
@ -183,6 +241,7 @@ nsCSPContext::~nsCSPContext()
|
||||
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
|
||||
delete mPolicies[i];
|
||||
}
|
||||
mShouldLoadCache.Clear();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -215,6 +274,8 @@ nsCSPContext::RemovePolicy(uint32_t aIndex)
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
mPolicies.RemoveElementAt(aIndex);
|
||||
// reset cache since effective policy changes
|
||||
mShouldLoadCache.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -237,6 +298,8 @@ nsCSPContext::AppendPolicy(const nsAString& aPolicyString,
|
||||
nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(aPolicyString, mSelfURI, aReportOnly, 0);
|
||||
if (policy) {
|
||||
mPolicies.AppendElement(policy);
|
||||
// reset cache since effective policy changes
|
||||
mShouldLoadCache.Clear();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -378,11 +441,118 @@ nsCSPContext::SetRequestContext(nsIURI* aSelfURI,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the given docshell, determines if this CSP context allows the
|
||||
* ancestry.
|
||||
*
|
||||
* In order to determine the URI of the parent document (one causing the load
|
||||
* of this protected document), this function obtains the docShellTreeItem,
|
||||
* then walks up the hierarchy until it finds a privileged (chrome) tree item.
|
||||
* Getting the parent's URI looks like this in pseudocode:
|
||||
*
|
||||
* nsIDocShell->QI(nsIInterfaceRequestor)
|
||||
* ->GI(nsIDocShellTreeItem)
|
||||
* ->QI(nsIInterfaceRequestor)
|
||||
* ->GI(nsIWebNavigation)
|
||||
* ->GetCurrentURI();
|
||||
*
|
||||
* aDocShell is the docShell for the protected document.
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell, bool* outPermitsAncestry)
|
||||
{
|
||||
// For now, we allows permitsAncestry, this will be fixed in Bug 994320
|
||||
nsresult rv;
|
||||
|
||||
// Can't check ancestry without a docShell.
|
||||
if (aDocShell == nullptr) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*outPermitsAncestry = true;
|
||||
|
||||
// extract the ancestry as an array
|
||||
nsCOMArray<nsIURI> ancestorsArray;
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(aDocShell));
|
||||
nsCOMPtr<nsIDocShellTreeItem> treeItem(do_GetInterface(ir));
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
|
||||
nsCOMPtr<nsIWebNavigation> webNav;
|
||||
nsCOMPtr<nsIURI> currentURI;
|
||||
nsCOMPtr<nsIURI> uriClone;
|
||||
|
||||
// iterate through each docShell parent item
|
||||
while (NS_SUCCEEDED(treeItem->GetParent(getter_AddRefs(parentTreeItem))) &&
|
||||
parentTreeItem != nullptr) {
|
||||
ir = do_QueryInterface(parentTreeItem);
|
||||
NS_ASSERTION(ir, "Could not QI docShellTreeItem to nsIInterfaceRequestor");
|
||||
|
||||
webNav = do_GetInterface(ir);
|
||||
NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
|
||||
|
||||
rv = webNav->GetCurrentURI(getter_AddRefs(currentURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (currentURI) {
|
||||
// stop when reaching chrome
|
||||
bool isChrome = false;
|
||||
rv = currentURI->SchemeIs("chrome", &isChrome);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (isChrome) { break; }
|
||||
|
||||
// delete the userpass from the URI.
|
||||
rv = currentURI->CloneIgnoringRef(getter_AddRefs(uriClone));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = uriClone->SetUserPass(EmptyCString());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
uriClone->GetSpec(spec);
|
||||
CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, found ancestor: %s", spec.get()));
|
||||
}
|
||||
#endif
|
||||
ancestorsArray.AppendElement(uriClone);
|
||||
}
|
||||
|
||||
// next ancestor
|
||||
treeItem = parentTreeItem;
|
||||
}
|
||||
|
||||
nsAutoString violatedDirective;
|
||||
|
||||
// Now that we've got the ancestry chain in ancestorsArray, time to check
|
||||
// them against any CSP.
|
||||
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
|
||||
for (uint32_t a = 0; a < ancestorsArray.Length(); a++) {
|
||||
// TODO(sid) the mapping from frame-ancestors context to TYPE_DOCUMENT is
|
||||
// forced. while this works for now, we will implement something in
|
||||
// bug 999656.
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
ancestorsArray[a]->GetSpec(spec);
|
||||
CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, checking ancestor: %s", spec.get()));
|
||||
}
|
||||
#endif
|
||||
if (!mPolicies[i]->permits(nsIContentPolicy::TYPE_DOCUMENT,
|
||||
ancestorsArray[a],
|
||||
EmptyString(), // no nonce
|
||||
violatedDirective)) {
|
||||
// Policy is violated
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
NS_ENSURE_TRUE(observerService, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
observerService->NotifyObservers(ancestorsArray[a],
|
||||
CSP_VIOLATION_TOPIC,
|
||||
violatedDirective.get());
|
||||
// TODO(sid) generate violation reports and remove NotifyObservers
|
||||
// call. (in bug 994322)
|
||||
// TODO(sid) implement logic for report-only (in bug 994322)
|
||||
*outPermitsAncestry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#define nsCSPContext_h___
|
||||
|
||||
#include "nsCSPUtils.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIClassInfo.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
@ -36,8 +37,9 @@ class nsCSPContext : public nsIContentSecurityPolicy
|
||||
bool* outShouldReportViolations,
|
||||
bool* outIsAllowed) const;
|
||||
|
||||
nsTArray<nsCSPPolicy*> mPolicies;
|
||||
nsCOMPtr<nsIURI> mSelfURI;
|
||||
nsTArray<nsCSPPolicy*> mPolicies;
|
||||
nsCOMPtr<nsIURI> mSelfURI;
|
||||
nsDataHashtable<nsCStringHashKey, int16_t> mShouldLoadCache;
|
||||
};
|
||||
|
||||
#endif /* nsCSPContext_h___ */
|
||||
|
@ -636,6 +636,8 @@ CSP_DirectiveToContentType(enum CSPDirective aDir)
|
||||
case CSP_MEDIA_SRC: return nsIContentPolicy::TYPE_MEDIA;
|
||||
case CSP_OBJECT_SRC: return nsIContentPolicy::TYPE_OBJECT;
|
||||
case CSP_FRAME_SRC: return nsIContentPolicy::TYPE_SUBDOCUMENT;
|
||||
// TODO(sid): fix this mapping to be more precise (bug 999656)
|
||||
case CSP_FRAME_ANCESTORS: return nsIContentPolicy::TYPE_DOCUMENT;
|
||||
|
||||
// Fall through to error for the following Directives:
|
||||
case CSP_DEFAULT_SRC:
|
||||
|
@ -49,22 +49,24 @@ enum CSPDirective {
|
||||
CSP_FONT_SRC,
|
||||
CSP_CONNECT_SRC,
|
||||
CSP_REPORT_URI,
|
||||
CSP_FRAME_ANCESTORS,
|
||||
// CSP_LAST_DIRECTIVE_VALUE always needs to be the last element in the enum
|
||||
// because we use it to calculate the size for the char* array.
|
||||
CSP_LAST_DIRECTIVE_VALUE
|
||||
};
|
||||
|
||||
static const char* CSPStrDirectives[] = {
|
||||
"default-src", // CSP_DEFAULT_SRC = 0
|
||||
"script-src", // CSP_SCRIPT_SRC
|
||||
"object-src", // CSP_OBJECT_SRC
|
||||
"style-src", // CSP_STYLE_SRC
|
||||
"img-src", // CSP_IMG_SRC
|
||||
"media-src", // CSP_MEDIA_SRC
|
||||
"frame-src", // CSP_FRAME_SRC
|
||||
"font-src", // CSP_FONT_SRC
|
||||
"connect-src", // CSP_CONNECT_SRC
|
||||
"report-uri", // CSP_REPORT_URI
|
||||
"default-src", // CSP_DEFAULT_SRC = 0
|
||||
"script-src", // CSP_SCRIPT_SRC
|
||||
"object-src", // CSP_OBJECT_SRC
|
||||
"style-src", // CSP_STYLE_SRC
|
||||
"img-src", // CSP_IMG_SRC
|
||||
"media-src", // CSP_MEDIA_SRC
|
||||
"frame-src", // CSP_FRAME_SRC
|
||||
"font-src", // CSP_FONT_SRC
|
||||
"connect-src", // CSP_CONNECT_SRC
|
||||
"report-uri", // CSP_REPORT_URI
|
||||
"frame-ancestors" // CSP_FRAME_ANCESTORS
|
||||
};
|
||||
|
||||
inline const char* CSP_EnumToDirective(enum CSPDirective aDir)
|
||||
|
@ -196,6 +196,7 @@ const char kLoadAsData[] = "loadAsData";
|
||||
nsIXPConnect *nsContentUtils::sXPConnect;
|
||||
nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
|
||||
nsIPrincipal *nsContentUtils::sSystemPrincipal;
|
||||
nsIPrincipal *nsContentUtils::sNullSubjectPrincipal;
|
||||
nsIParserService *nsContentUtils::sParserService = nullptr;
|
||||
nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
|
||||
nsIIOService *nsContentUtils::sIOService;
|
||||
@ -379,6 +380,7 @@ nsContentUtils::Init()
|
||||
|
||||
sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
|
||||
MOZ_ASSERT(sSystemPrincipal);
|
||||
NS_ADDREF(sNullSubjectPrincipal = new nsNullPrincipal());
|
||||
|
||||
nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
|
||||
if (NS_FAILED(rv)) {
|
||||
@ -1436,6 +1438,7 @@ nsContentUtils::Shutdown()
|
||||
sXPConnect = nullptr;
|
||||
NS_IF_RELEASE(sSecurityManager);
|
||||
NS_IF_RELEASE(sSystemPrincipal);
|
||||
NS_IF_RELEASE(sNullSubjectPrincipal);
|
||||
NS_IF_RELEASE(sParserService);
|
||||
NS_IF_RELEASE(sIOService);
|
||||
NS_IF_RELEASE(sLineBreaker);
|
||||
@ -2322,9 +2325,33 @@ nsContentUtils::SubjectPrincipal()
|
||||
return GetSystemPrincipal();
|
||||
}
|
||||
|
||||
JSCompartment* compartment = js::GetContextCompartment(cx);
|
||||
MOZ_ASSERT(compartment);
|
||||
JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
|
||||
JSCompartment *compartment = js::GetContextCompartment(cx);
|
||||
|
||||
// When an AutoJSAPI is instantiated, we are in a null compartment until the
|
||||
// first JSAutoCompartment, which is kind of a purgatory as far as permissions
|
||||
// go. It would be nice to just hard-abort if somebody does a security check
|
||||
// in this purgatory zone, but that would be too fragile, since it could be
|
||||
// triggered by random IsCallerChrome() checks 20-levels deep.
|
||||
//
|
||||
// So we want to return _something_ here - and definitely not the System
|
||||
// Principal, since that would make an AutoJSAPI a very dangerous thing to
|
||||
// instantiate.
|
||||
//
|
||||
// The natural thing to return is a null principal. Ideally, we'd return a
|
||||
// different null principal each time, to avoid any unexpected interactions
|
||||
// when the principal accidentally gets inherited somewhere. But
|
||||
// GetSubjectPrincipal doesn't return strong references, so there's no way to
|
||||
// sanely manage the lifetime of multiple null principals.
|
||||
//
|
||||
// So we use a singleton null principal. To avoid it being accidentally
|
||||
// inherited and becoming a "real" subject or object principal, we do a
|
||||
// release-mode assert during compartment creation against using this
|
||||
// principal on an actual global.
|
||||
if (!compartment) {
|
||||
return sNullSubjectPrincipal;
|
||||
}
|
||||
|
||||
JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
|
||||
return nsJSPrincipals::get(principals);
|
||||
}
|
||||
|
||||
|
@ -667,6 +667,28 @@ nsresult TestGoodGeneratedPolicies() {
|
||||
"script-src http://policy-uri" },
|
||||
{ "img-src 'self'; ",
|
||||
"img-src http://www.selfuri.com" },
|
||||
{ "frame-ancestors foo-bar.com",
|
||||
"frame-ancestors http://foo-bar.com" },
|
||||
{ "frame-ancestors http://a.com",
|
||||
"frame-ancestors http://a.com" },
|
||||
{ "frame-ancestors 'self'",
|
||||
"frame-ancestors http://www.selfuri.com" },
|
||||
{ "frame-ancestors http://self.com:88",
|
||||
"frame-ancestors http://self.com:88" },
|
||||
{ "frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com",
|
||||
"frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com" },
|
||||
{ "frame-ancestors https://self.com:34",
|
||||
"frame-ancestors https://self.com:34" },
|
||||
{ "default-src 'none'; frame-ancestors 'self'",
|
||||
"default-src 'none'; frame-ancestors http://www.selfuri.com" },
|
||||
{ "frame-ancestors http://self:80",
|
||||
"frame-ancestors http://self:80" },
|
||||
{ "frame-ancestors http://self.com/bar",
|
||||
"frame-ancestors http://self.com" },
|
||||
{ "default-src 'self'; frame-ancestors 'self'",
|
||||
"default-src http://www.selfuri.com; frame-ancestors http://www.selfuri.com" },
|
||||
{ "frame-ancestors http://bar.com/foo.png",
|
||||
"frame-ancestors http://bar.com" },
|
||||
};
|
||||
|
||||
uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
|
||||
|
@ -154,15 +154,6 @@ fuzzy(1,65536) random-if(gtk2Widget) fails-if(B2G) fuzzy-if(Android,9,65536) fai
|
||||
# random-if(B2G) from bug 983650
|
||||
fuzzy(1,65536) random-if(B2G) fuzzy-if(Android,9,65536) random-if(Android&&AndroidVersion<15) == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=0.5&alpha&premult wrapper.html?colors-half-alpha.png
|
||||
|
||||
# Test over-bright premult:
|
||||
# random-if(B2G) from bug 983650
|
||||
# This test assumes that the compositor is able to correctly handle superluminant
|
||||
# pixels in a WebGL canvas with premultiplied alpha. This is not handled by
|
||||
# Skia's software blending code. Consequently, this test is random on
|
||||
# basic-layers when using skia. See bug 1004483.
|
||||
fuzzy(1,65536) random-if(B2G) fuzzy-if(Android,9,65536) random-if(Android&&AndroidVersion<15) random-if(skiaContent&&!layersGPUAccelerated) == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.5&alpha&premult wrapper.html?colors-half-alpha.png
|
||||
|
||||
|
||||
# Check for hanging framebuffer bindings:
|
||||
random-if(Android&&AndroidVersion<15) == webgl-hanging-fb-test.html?__&________ wrapper.html?green.png
|
||||
random-if(Android&&AndroidVersion<15) == webgl-hanging-fb-test.html?aa&________ wrapper.html?green.png
|
||||
|
@ -6,43 +6,10 @@
|
||||
|
||||
#include "mozilla/dom/HTMLAllCollection.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "mozilla/dom/HTMLAllCollectionBinding.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "nsHTMLDocument.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
class nsHTMLDocumentSH
|
||||
{
|
||||
public:
|
||||
static bool DocumentAllGetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::Value> vp);
|
||||
static bool DocumentAllNewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JSObject*> objp);
|
||||
static void ReleaseDocument(JSFreeOp *fop, JSObject *obj);
|
||||
static bool CallToGetPropMapper(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||
};
|
||||
|
||||
const JSClass sHTMLDocumentAllClass = {
|
||||
"HTML document.all class",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_NEW_RESOLVE |
|
||||
JSCLASS_EMULATES_UNDEFINED,
|
||||
JS_PropertyStub, /* addProperty */
|
||||
JS_DeletePropertyStub, /* delProperty */
|
||||
nsHTMLDocumentSH::DocumentAllGetProperty, /* getProperty */
|
||||
JS_StrictPropertyStub, /* setProperty */
|
||||
JS_EnumerateStub,
|
||||
(JSResolveOp)nsHTMLDocumentSH::DocumentAllNewResolve,
|
||||
JS_ConvertStub,
|
||||
nsHTMLDocumentSH::ReleaseDocument,
|
||||
nsHTMLDocumentSH::CallToGetPropMapper
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -51,37 +18,31 @@ HTMLAllCollection::HTMLAllCollection(nsHTMLDocument* aDocument)
|
||||
: mDocument(aDocument)
|
||||
{
|
||||
MOZ_ASSERT(mDocument);
|
||||
mozilla::HoldJSObjects(this);
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
HTMLAllCollection::~HTMLAllCollection()
|
||||
{
|
||||
mObject = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(HTMLAllCollection, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(HTMLAllCollection, Release)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLAllCollection,
|
||||
mDocument,
|
||||
mCollection,
|
||||
mNamedMap)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAllCollection)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLAllCollection)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLAllCollection)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLAllCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNamedMap)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLAllCollection)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLAllCollection)
|
||||
tmp->mObject = nullptr;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNamedMap)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLAllCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
nsINode*
|
||||
HTMLAllCollection::GetParentObject() const
|
||||
{
|
||||
return mDocument;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
HTMLAllCollection::Length()
|
||||
@ -95,32 +56,6 @@ HTMLAllCollection::Item(uint32_t aIndex)
|
||||
return Collection()->Item(aIndex);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
HTMLAllCollection::GetObject(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
|
||||
if (!mObject) {
|
||||
JS::Rooted<JSObject*> wrapper(aCx, mDocument->GetWrapper());
|
||||
MOZ_ASSERT(wrapper);
|
||||
|
||||
JSAutoCompartment ac(aCx, wrapper);
|
||||
JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, wrapper));
|
||||
mObject = JS_NewObject(aCx, &sHTMLDocumentAllClass, JS::NullPtr(), global);
|
||||
if (!mObject) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make the JSObject hold a reference to the document.
|
||||
JS_SetPrivate(mObject, ToSupports(mDocument));
|
||||
NS_ADDREF(mDocument);
|
||||
}
|
||||
|
||||
JS::ExposeObjectToActiveJS(mObject);
|
||||
return mObject;
|
||||
}
|
||||
|
||||
nsContentList*
|
||||
HTMLAllCollection::Collection()
|
||||
{
|
||||
@ -186,197 +121,43 @@ HTMLAllCollection::GetDocumentAllList(const nsAString& aID)
|
||||
return docAllList;
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
HTMLAllCollection::GetNamedItem(const nsAString& aID,
|
||||
nsWrapperCache** aCache)
|
||||
void
|
||||
HTMLAllCollection::NamedGetter(const nsAString& aID,
|
||||
bool& aFound,
|
||||
Nullable<OwningNodeOrHTMLCollection>& aResult)
|
||||
{
|
||||
nsContentList* docAllList = GetDocumentAllList(aID);
|
||||
if (!docAllList) {
|
||||
return nullptr;
|
||||
aFound = false;
|
||||
aResult.SetNull();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there are more than 1 entries. Do this by getting the second one
|
||||
// rather than the length since getting the length always requires walking
|
||||
// the entire document.
|
||||
|
||||
nsIContent* cont = docAllList->Item(1, true);
|
||||
if (cont) {
|
||||
*aCache = docAllList;
|
||||
return static_cast<nsINodeList*>(docAllList);
|
||||
if (docAllList->Item(1, true)) {
|
||||
aFound = true;
|
||||
aResult.SetValue().SetAsHTMLCollection() = docAllList;
|
||||
return;
|
||||
}
|
||||
|
||||
// There's only 0 or 1 items. Return the first one or null.
|
||||
*aCache = cont = docAllList->Item(0, true);
|
||||
return cont;
|
||||
if (nsIContent* node = docAllList->Item(0, true)) {
|
||||
aFound = true;
|
||||
aResult.SetValue().SetAsNode() = node;
|
||||
return;
|
||||
}
|
||||
|
||||
aFound = false;
|
||||
aResult.SetNull();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
HTMLAllCollection::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return HTMLAllCollectionBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
static nsHTMLDocument*
|
||||
GetDocument(JSObject *obj)
|
||||
{
|
||||
MOZ_ASSERT(js::GetObjectJSClass(obj) == &sHTMLDocumentAllClass);
|
||||
return static_cast<nsHTMLDocument*>(
|
||||
static_cast<nsINode*>(JS_GetPrivate(obj)));
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JS::Handle<JSObject*> obj_,
|
||||
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(cx, obj_);
|
||||
|
||||
// document.all.item and .namedItem get their value in the
|
||||
// newResolve hook, so nothing to do for those properties here. And
|
||||
// we need to return early to prevent <div id="item"> from shadowing
|
||||
// document.all.item(), etc.
|
||||
if (nsDOMClassInfo::sItem_id == id || nsDOMClassInfo::sNamedItem_id == id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
while (js::GetObjectJSClass(obj) != &sHTMLDocumentAllClass) {
|
||||
if (!js::GetObjectProto(cx, obj, &proto)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!proto) {
|
||||
NS_ERROR("The JS engine lies!");
|
||||
return true;
|
||||
}
|
||||
|
||||
obj = proto;
|
||||
}
|
||||
|
||||
HTMLAllCollection* allCollection = GetDocument(obj)->All();
|
||||
nsISupports *result;
|
||||
nsWrapperCache *cache;
|
||||
|
||||
if (JSID_IS_STRING(id)) {
|
||||
if (nsDOMClassInfo::sLength_id == id) {
|
||||
// Make sure <div id="length"> doesn't shadow document.all.length.
|
||||
vp.setNumber(allCollection->Length());
|
||||
return true;
|
||||
}
|
||||
|
||||
// For all other strings, look for an element by id or name.
|
||||
nsDependentJSString str(id);
|
||||
result = allCollection->GetNamedItem(str, &cache);
|
||||
} else if (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) {
|
||||
// Map document.all[n] (where n is a number) to the n:th item in
|
||||
// the document.all node list.
|
||||
|
||||
nsIContent* node = allCollection->Item(SafeCast<uint32_t>(JSID_TO_INT(id)));
|
||||
|
||||
result = node;
|
||||
cache = node;
|
||||
} else {
|
||||
result = nullptr;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
nsresult rv = nsContentUtils::WrapNative(cx, result, cache, vp);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(cx, rv);
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
vp.setUndefined();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLDocumentSH::DocumentAllNewResolve(JSContext *cx, JS::Handle<JSObject*> obj,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JSObject*> objp)
|
||||
{
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
|
||||
if (nsDOMClassInfo::sItem_id == id || nsDOMClassInfo::sNamedItem_id == id) {
|
||||
// Define the item() or namedItem() method.
|
||||
|
||||
JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallToGetPropMapper,
|
||||
0, JSPROP_ENUMERATE);
|
||||
objp.set(obj);
|
||||
|
||||
return fnc != nullptr;
|
||||
}
|
||||
|
||||
if (nsDOMClassInfo::sLength_id == id) {
|
||||
// document.all.length. Any jsval other than undefined would do
|
||||
// here, all we need is to get into the code below that defines
|
||||
// this propery on obj, the rest happens in
|
||||
// DocumentAllGetProperty().
|
||||
|
||||
v = JSVAL_ONE;
|
||||
} else {
|
||||
if (!DocumentAllGetProperty(cx, obj, id, &v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
|
||||
if (v.get() != JSVAL_VOID) {
|
||||
ok = ::JS_DefinePropertyById(cx, obj, id, v, 0);
|
||||
objp.set(obj);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLDocumentSH::ReleaseDocument(JSFreeOp *fop, JSObject *obj)
|
||||
{
|
||||
nsIHTMLDocument* doc = GetDocument(obj);
|
||||
if (doc) {
|
||||
nsContentUtils::DeferredFinalize(doc);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
// Handle document.all("foo") style access to document.all.
|
||||
|
||||
if (args.length() != 1) {
|
||||
// XXX: Should throw NS_ERROR_XPC_NOT_ENOUGH_ARGS for argc < 1,
|
||||
// and create a new NS_ERROR_XPC_TOO_MANY_ARGS for argc > 1? IE
|
||||
// accepts nothing other than one arg.
|
||||
xpc::Throw(cx, NS_ERROR_INVALID_ARG);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert all types to string.
|
||||
JS::Rooted<JSString*> str(cx, JS::ToString(cx, args[0]));
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are called via document.all(id) instead of document.all.item(i) or
|
||||
// another method, use the document.all callee object as self.
|
||||
JS::Rooted<JSObject*> self(cx);
|
||||
if (args.calleev().isObject() &&
|
||||
JS_GetClass(&args.calleev().toObject()) == &sHTMLDocumentAllClass) {
|
||||
self = &args.calleev().toObject();
|
||||
} else {
|
||||
self = JS_THIS_OBJECT(cx, vp);
|
||||
if (!self)
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t length;
|
||||
JS::Anchor<JSString *> anchor(str);
|
||||
const jschar *chars = ::JS_GetStringCharsAndLength(cx, str, &length);
|
||||
if (!chars) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ::JS_GetUCProperty(cx, self, chars, length, args.rval());
|
||||
}
|
||||
|
@ -7,49 +7,84 @@
|
||||
#ifndef mozilla_dom_HTMLAllCollection_h
|
||||
#define mozilla_dom_HTMLAllCollection_h
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class nsContentList;
|
||||
class nsHTMLDocument;
|
||||
class nsIContent;
|
||||
class nsWrapperCache;
|
||||
class nsINode;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class HTMLAllCollection
|
||||
class OwningNodeOrHTMLCollection;
|
||||
template<typename> class Nullable;
|
||||
|
||||
class HTMLAllCollection MOZ_FINAL : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
HTMLAllCollection(nsHTMLDocument* aDocument);
|
||||
~HTMLAllCollection();
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(HTMLAllCollection)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(HTMLAllCollection)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(HTMLAllCollection)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
nsINode* GetParentObject() const;
|
||||
|
||||
uint32_t Length();
|
||||
nsIContent* Item(uint32_t aIndex);
|
||||
void Item(const nsAString& aName, Nullable<OwningNodeOrHTMLCollection>& aResult)
|
||||
{
|
||||
NamedItem(aName, aResult);
|
||||
}
|
||||
nsIContent* IndexedGetter(uint32_t aIndex, bool& aFound)
|
||||
{
|
||||
nsIContent* result = Item(aIndex);
|
||||
aFound = !!result;
|
||||
return result;
|
||||
}
|
||||
|
||||
JSObject* GetObject(JSContext* aCx, ErrorResult& aRv);
|
||||
|
||||
nsISupports* GetNamedItem(const nsAString& aID, nsWrapperCache** aCache);
|
||||
void NamedItem(const nsAString& aName,
|
||||
Nullable<OwningNodeOrHTMLCollection>& aResult)
|
||||
{
|
||||
bool found = false;
|
||||
NamedGetter(aName, found, aResult);
|
||||
}
|
||||
void NamedGetter(const nsAString& aName,
|
||||
bool& aFound,
|
||||
Nullable<OwningNodeOrHTMLCollection>& aResult);
|
||||
void GetSupportedNames(unsigned flags, nsTArray<nsString>& aNames)
|
||||
{
|
||||
}
|
||||
bool NameIsEnumerable(const nsAString& aName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void LegacyCall(JS::Handle<JS::Value>, const nsAString& aName,
|
||||
Nullable<OwningNodeOrHTMLCollection>& aResult)
|
||||
{
|
||||
NamedItem(aName, aResult);
|
||||
}
|
||||
|
||||
private:
|
||||
nsContentList* Collection();
|
||||
|
||||
/**
|
||||
* Returns the NodeList for document.all[aID], or null if there isn't one.
|
||||
* Returns the HTMLCollection for document.all[aID], or null if there isn't one.
|
||||
*/
|
||||
nsContentList* GetDocumentAllList(const nsAString& aID);
|
||||
|
||||
JS::Heap<JSObject*> mObject;
|
||||
nsRefPtr<nsHTMLDocument> mDocument;
|
||||
nsRefPtr<nsContentList> mCollection;
|
||||
nsRefPtrHashtable<nsStringHashKey, nsContentList> mNamedMap;
|
||||
|
@ -2564,12 +2564,6 @@ nsHTMLDocument::All()
|
||||
return mAll;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
nsHTMLDocument::GetAll(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return All()->GetObject(aCx, aRv);
|
||||
}
|
||||
|
||||
static void
|
||||
NotifyEditableStateChange(nsINode *aNode, nsIDocument *aDocument)
|
||||
{
|
||||
|
@ -104,7 +104,6 @@ public:
|
||||
NS_DECL_NSIDOMHTMLDOCUMENT
|
||||
|
||||
mozilla::dom::HTMLAllCollection* All();
|
||||
JSObject* GetAll(JSContext* aCx, mozilla::ErrorResult& aRv);
|
||||
|
||||
nsISupports* ResolveName(const nsAString& aName, nsWrapperCache **aCache);
|
||||
|
||||
|
@ -78,16 +78,17 @@ public:
|
||||
int64_t aEndTime,
|
||||
int64_t aCurrentTime) = 0;
|
||||
|
||||
// Called to move the reader into idle/active state. When the reader is
|
||||
// Called to move the reader into idle state. When the reader is
|
||||
// created it is assumed to be active (i.e. not idle). When the media
|
||||
// element is paused and we don't need to decode any more data, the state
|
||||
// machine calls SetIdle() to inform the reader that its decoder won't be
|
||||
// needed for a while. When we need to decode data again, the state machine
|
||||
// calls SetActive() to activate the decoder. The reader can use these
|
||||
// notifications to enter/exit a low power state when the decoder isn't
|
||||
// needed, if desired. This is most useful on mobile.
|
||||
// needed for a while. The reader can use these notifications to enter
|
||||
// a low power state when the decoder isn't needed, if desired.
|
||||
// This is most useful on mobile.
|
||||
// Note: DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek should
|
||||
// activate the decoder if necessary. The state machine only needs to know
|
||||
// when to call SetIdle().
|
||||
virtual void SetIdle() { }
|
||||
virtual void SetActive() { }
|
||||
|
||||
// Tell the reader that the data decoded are not for direct playback, so it
|
||||
// can accept more files, in particular those which have more channels than
|
||||
|
@ -194,7 +194,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
mAmpleAudioThresholdUsecs(AMPLE_AUDIO_USECS),
|
||||
mDispatchedAudioDecodeTask(false),
|
||||
mDispatchedVideoDecodeTask(false),
|
||||
mIsReaderIdle(false),
|
||||
mAudioCaptured(false),
|
||||
mTransportSeekable(true),
|
||||
mMediaSeekable(true),
|
||||
@ -573,7 +572,6 @@ MediaDecoderStateMachine::DecodeVideo()
|
||||
mDispatchedVideoDecodeTask = false;
|
||||
return;
|
||||
}
|
||||
EnsureActive();
|
||||
|
||||
// We don't want to consider skipping to the next keyframe if we've
|
||||
// only just started up the decode loop, so wait until we've decoded
|
||||
@ -667,7 +665,6 @@ MediaDecoderStateMachine::DecodeAudio()
|
||||
mDispatchedAudioDecodeTask = false;
|
||||
return;
|
||||
}
|
||||
EnsureActive();
|
||||
|
||||
// We don't want to consider skipping to the next keyframe if we've
|
||||
// only just started up the decode loop, so wait until we've decoded
|
||||
@ -1499,21 +1496,6 @@ MediaDecoderStateMachine::EnqueueDecodeMetadataTask()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::EnsureActive()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
if (!mIsReaderIdle) {
|
||||
return;
|
||||
}
|
||||
mIsReaderIdle = false;
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
SetReaderActive();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::SetReaderIdle()
|
||||
{
|
||||
@ -1529,14 +1511,6 @@ MediaDecoderStateMachine::SetReaderIdle()
|
||||
mReader->SetIdle();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::SetReaderActive()
|
||||
{
|
||||
DECODER_LOG(PR_LOG_DEBUG, "SetReaderActive()");
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
mReader->SetActive();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
|
||||
{
|
||||
@ -1575,19 +1549,13 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
|
||||
EnsureVideoDecodeTaskQueued();
|
||||
}
|
||||
|
||||
if (mIsReaderIdle == needIdle) {
|
||||
return;
|
||||
}
|
||||
mIsReaderIdle = needIdle;
|
||||
RefPtr<nsIRunnable> event;
|
||||
if (mIsReaderIdle) {
|
||||
event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderIdle);
|
||||
} else {
|
||||
event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderActive);
|
||||
}
|
||||
if (NS_FAILED(mDecodeTaskQueue->Dispatch(event.forget())) &&
|
||||
mState != DECODER_STATE_SHUTDOWN) {
|
||||
NS_WARNING("Failed to dispatch event to set decoder idle state");
|
||||
if (needIdle) {
|
||||
RefPtr<nsIRunnable> event = NS_NewRunnableMethod(
|
||||
this, &MediaDecoderStateMachine::SetReaderIdle);
|
||||
nsresult rv = mDecodeTaskQueue->Dispatch(event.forget());
|
||||
if (NS_FAILED(rv) && mState != DECODER_STATE_SHUTDOWN) {
|
||||
NS_WARNING("Failed to dispatch event to set decoder idle state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1828,7 +1796,6 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
||||
if (mState != DECODER_STATE_DECODING_METADATA) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
EnsureActive();
|
||||
|
||||
nsresult res;
|
||||
MediaInfo info;
|
||||
@ -1930,7 +1897,6 @@ void MediaDecoderStateMachine::DecodeSeek()
|
||||
if (mState != DECODER_STATE_SEEKING) {
|
||||
return;
|
||||
}
|
||||
EnsureActive();
|
||||
|
||||
// During the seek, don't have a lock on the decoder state,
|
||||
// otherwise long seek operations can block the main thread.
|
||||
|
@ -565,11 +565,9 @@ protected:
|
||||
// The decoder monitor must be held.
|
||||
nsresult EnqueueDecodeSeekTask();
|
||||
|
||||
// Calls the reader's SetIdle(), with aIsIdle as parameter. This is only
|
||||
// called in a task dispatched to the decode task queue, don't call it
|
||||
// directly.
|
||||
// Calls the reader's SetIdle(). This is only called in a task dispatched to
|
||||
// the decode task queue, don't call it directly.
|
||||
void SetReaderIdle();
|
||||
void SetReaderActive();
|
||||
|
||||
// Re-evaluates the state and determines whether we need to dispatch
|
||||
// events to run the decode, or if not whether we should set the reader
|
||||
@ -577,11 +575,6 @@ protected:
|
||||
// The decoder monitor must be held.
|
||||
void DispatchDecodeTasksIfNeeded();
|
||||
|
||||
// Called before we do anything on the decode task queue to set the reader
|
||||
// as not idle if it was idle. This is called before we decode, seek, or
|
||||
// decode metadata (in case we were dormant or awaiting resources).
|
||||
void EnsureActive();
|
||||
|
||||
// Queries our state to see whether the decode has finished for all streams.
|
||||
// If so, we move into DECODER_STATE_COMPLETED and schedule the state machine
|
||||
// to run.
|
||||
@ -851,12 +844,6 @@ protected:
|
||||
// the video decode.
|
||||
bool mDispatchedVideoDecodeTask;
|
||||
|
||||
// True when the reader is initialized, but has been ordered "idle" by the
|
||||
// state machine. This happens when the MediaQueue's of decoded data are
|
||||
// "full" and playback is paused. The reader may choose to use the idle
|
||||
// notification to enter a low power state.
|
||||
bool mIsReaderIdle;
|
||||
|
||||
// If the video decode is falling behind the audio, we'll start dropping the
|
||||
// inter-frames up until the next keyframe which is at or before the current
|
||||
// playback position. skipToNextKeyframe is true if we're currently
|
||||
|
@ -147,7 +147,6 @@ private:
|
||||
mActiveVideoDecoder = i;
|
||||
MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
|
||||
|
||||
GetVideoReader()->SetActive();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -326,7 +325,6 @@ MediaSourceReader::CallDecoderInitialization()
|
||||
MediaDecoderReader* reader = decoder->GetReader();
|
||||
MSE_DEBUG("%p: Initializating subdecoder %p reader %p", this, decoder.get(), reader);
|
||||
|
||||
reader->SetActive();
|
||||
MediaInfo mi;
|
||||
nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
|
||||
nsresult rv;
|
||||
@ -456,8 +454,6 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
||||
MediaDecoderReader* reader = mDecoders[i]->GetReader();
|
||||
|
||||
reader->SetActive(); // XXX check where this should be called
|
||||
|
||||
MediaInfo mi = reader->GetMediaInfo();
|
||||
|
||||
if (mi.HasVideo() && !mInfo.HasVideo()) {
|
||||
|
@ -47,9 +47,6 @@ MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder)
|
||||
, mVideoSeekTimeUs(-1)
|
||||
, mAudioSeekTimeUs(-1)
|
||||
, mSkipCount(0)
|
||||
#ifdef DEBUG
|
||||
, mIsActive(true)
|
||||
#endif
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
if (!gMediaDecoderLog) {
|
||||
@ -135,7 +132,7 @@ nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
MOZ_ASSERT(mIsActive);
|
||||
EnsureActive();
|
||||
|
||||
*aTags = nullptr;
|
||||
|
||||
@ -211,7 +208,8 @@ nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
|
||||
bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
MOZ_ASSERT(mIsActive);
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
EnsureActive();
|
||||
|
||||
// Record number of frames decoded and parsed. Automatically update the
|
||||
// stats counters using the AutoNotifyDecoded stack-based class.
|
||||
@ -341,7 +339,7 @@ void MediaOmxReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, in
|
||||
bool MediaOmxReader::DecodeAudioData()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
MOZ_ASSERT(mIsActive);
|
||||
EnsureActive();
|
||||
|
||||
// This is the approximate byte position in the stream.
|
||||
int64_t pos = mDecoder->GetResource()->Tell();
|
||||
@ -375,7 +373,7 @@ bool MediaOmxReader::DecodeAudioData()
|
||||
nsresult MediaOmxReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
MOZ_ASSERT(mIsActive);
|
||||
EnsureActive();
|
||||
|
||||
ResetDecode();
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
@ -410,19 +408,13 @@ static uint64_t BytesToTime(int64_t offset, uint64_t length, uint64_t durationUs
|
||||
}
|
||||
|
||||
void MediaOmxReader::SetIdle() {
|
||||
#ifdef DEBUG
|
||||
mIsActive = false;
|
||||
#endif
|
||||
if (!mOmxDecoder.get()) {
|
||||
return;
|
||||
}
|
||||
mOmxDecoder->Pause();
|
||||
}
|
||||
|
||||
void MediaOmxReader::SetActive() {
|
||||
#ifdef DEBUG
|
||||
mIsActive = true;
|
||||
#endif
|
||||
void MediaOmxReader::EnsureActive() {
|
||||
if (!mOmxDecoder.get()) {
|
||||
return;
|
||||
}
|
||||
|
@ -49,6 +49,10 @@ protected:
|
||||
// information used for creating OMX decoder such as video/audio codec.
|
||||
virtual nsresult InitOmxDecoder();
|
||||
|
||||
// Called inside DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek
|
||||
// to activate the decoder automatically.
|
||||
virtual void EnsureActive();
|
||||
|
||||
public:
|
||||
MediaOmxReader(AbstractMediaDecoder* aDecoder);
|
||||
~MediaOmxReader();
|
||||
@ -83,7 +87,6 @@ public:
|
||||
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
|
||||
|
||||
virtual void SetIdle() MOZ_OVERRIDE;
|
||||
virtual void SetActive() MOZ_OVERRIDE;
|
||||
|
||||
void SetAudioChannel(dom::AudioChannel aAudioChannel) {
|
||||
mAudioChannel = aAudioChannel;
|
||||
@ -99,12 +102,6 @@ public:
|
||||
// ANDROID_VERSION < 19
|
||||
void CheckAudioOffload();
|
||||
#endif
|
||||
|
||||
private:
|
||||
// This flag is true when SetActive() has been called without a matching
|
||||
// SetIdle(). This is used to sanity check the SetIdle/SetActive calls, to
|
||||
// ensure SetActive has been called before a decode call.
|
||||
DebugOnly<bool> mIsActive;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -779,6 +779,7 @@ bool OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aTimeUs,
|
||||
if (aDoSeek) {
|
||||
{
|
||||
Mutex::Autolock autoLock(mSeekLock);
|
||||
ReleaseAllPendingVideoBuffersLocked();
|
||||
mIsVideoSeeking = true;
|
||||
}
|
||||
MediaSource::ReadOptions options;
|
||||
|
@ -299,18 +299,6 @@ nsresult RtspOmxReader::Seek(int64_t aTime, int64_t aStartTime,
|
||||
return MediaOmxReader::Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
}
|
||||
|
||||
nsresult
|
||||
RtspOmxReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
SetActive();
|
||||
|
||||
nsresult rv = MediaOmxReader::ReadMetadata(aInfo, aTags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void RtspOmxReader::SetIdle() {
|
||||
// Call parent class to set OMXCodec idle.
|
||||
MediaOmxReader::SetIdle();
|
||||
@ -326,7 +314,7 @@ void RtspOmxReader::SetIdle() {
|
||||
}
|
||||
}
|
||||
|
||||
void RtspOmxReader::SetActive() {
|
||||
void RtspOmxReader::EnsureActive() {
|
||||
// Need to start RTSP streaming OMXCodec decoding.
|
||||
if (mRtspResource) {
|
||||
nsIStreamingProtocolController* controller =
|
||||
@ -338,7 +326,7 @@ void RtspOmxReader::SetActive() {
|
||||
}
|
||||
|
||||
// Call parent class to set OMXCodec active.
|
||||
MediaOmxReader::SetActive();
|
||||
MediaOmxReader::EnsureActive();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -28,6 +28,7 @@ class RtspOmxReader : public MediaOmxReader
|
||||
protected:
|
||||
// Provide a Rtsp extractor.
|
||||
nsresult InitOmxDecoder() MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void EnsureActive() MOZ_OVERRIDE;
|
||||
|
||||
public:
|
||||
RtspOmxReader(AbstractMediaDecoder* aDecoder)
|
||||
@ -44,9 +45,6 @@ public:
|
||||
MOZ_COUNT_DTOR(RtspOmxReader);
|
||||
}
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) MOZ_OVERRIDE;
|
||||
|
||||
// Implement a time-based seek instead of byte-based..
|
||||
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
|
||||
int64_t aCurrentTime) MOZ_FINAL MOZ_OVERRIDE;
|
||||
@ -66,7 +64,6 @@ public:
|
||||
}
|
||||
|
||||
virtual void SetIdle() MOZ_OVERRIDE;
|
||||
virtual void SetActive() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// A pointer to RtspMediaResource for calling the Rtsp specific function.
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/dom/SVGAnimatedTransformList.h"
|
||||
#include "mozilla/dom/SVGGraphicsElementBinding.h"
|
||||
#include "mozilla/dom/SVGTransformableElement.h"
|
||||
#include "mozilla/dom/SVGMatrix.h"
|
||||
#include "mozilla/dom/SVGSVGElement.h"
|
||||
@ -182,7 +183,8 @@ SVGTransformableElement::GetFarthestViewportElement()
|
||||
}
|
||||
|
||||
already_AddRefed<SVGIRect>
|
||||
SVGTransformableElement::GetBBox(ErrorResult& rv)
|
||||
SVGTransformableElement::GetBBox(const SVGBoundingBoxOptions& aOptions,
|
||||
ErrorResult& rv)
|
||||
{
|
||||
nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
|
||||
|
||||
@ -190,14 +192,37 @@ SVGTransformableElement::GetBBox(ErrorResult& rv)
|
||||
rv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsISVGChildFrame* svgframe = do_QueryFrame(frame);
|
||||
if (!svgframe) {
|
||||
rv.Throw(NS_ERROR_NOT_IMPLEMENTED); // XXX: outer svg
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame)));
|
||||
if (!NS_SVGNewGetBBoxEnabled()) {
|
||||
return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame)));
|
||||
} else {
|
||||
uint32_t aFlags = 0;
|
||||
if (aOptions.mFill) {
|
||||
aFlags |= nsSVGUtils::eBBoxIncludeFill;
|
||||
}
|
||||
if (aOptions.mStroke) {
|
||||
aFlags |= nsSVGUtils::eBBoxIncludeStroke;
|
||||
}
|
||||
if (aOptions.mMarkers) {
|
||||
aFlags |= nsSVGUtils::eBBoxIncludeMarkers;
|
||||
}
|
||||
if (aOptions.mClipped) {
|
||||
aFlags |= nsSVGUtils::eBBoxIncludeClipped;
|
||||
}
|
||||
if (aFlags == 0) {
|
||||
return NS_NewSVGRect(this,0,0,0,0);
|
||||
}
|
||||
if (aFlags == nsSVGUtils::eBBoxIncludeMarkers ||
|
||||
aFlags == nsSVGUtils::eBBoxIncludeClipped) {
|
||||
aFlags |= nsSVGUtils::eBBoxIncludeFill;
|
||||
}
|
||||
return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame, aFlags)));
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<SVGMatrix>
|
||||
|
@ -19,6 +19,7 @@ class SVGAnimatedTransformList;
|
||||
class SVGGraphicsElement;
|
||||
class SVGMatrix;
|
||||
class SVGIRect;
|
||||
class SVGBoundingBoxOptions;
|
||||
|
||||
class SVGTransformableElement : public nsSVGElement
|
||||
{
|
||||
@ -33,7 +34,8 @@ public:
|
||||
already_AddRefed<SVGAnimatedTransformList> Transform();
|
||||
nsSVGElement* GetNearestViewportElement();
|
||||
nsSVGElement* GetFarthestViewportElement();
|
||||
already_AddRefed<SVGIRect> GetBBox(ErrorResult& rv);
|
||||
already_AddRefed<SVGIRect> GetBBox(const SVGBoundingBoxOptions& aOptions,
|
||||
ErrorResult& rv);
|
||||
already_AddRefed<SVGMatrix> GetCTM();
|
||||
already_AddRefed<SVGMatrix> GetScreenCTM();
|
||||
already_AddRefed<SVGMatrix> GetTransformToElement(SVGGraphicsElement& aElement,
|
||||
|
299
content/svg/content/test/getBBox-method-helper.svg
Normal file
299
content/svg/content/test/getBBox-method-helper.svg
Normal file
@ -0,0 +1,299 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 500 500" width="500px" height="500px">
|
||||
<defs>
|
||||
<clipPath id="rect01" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
|
||||
<rect x="0" y="0" width="0.5" height="1.0"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect02" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
|
||||
<rect x="0.5" y="0" width="0.5" height="1.0"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect03" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
|
||||
<rect x="0.5" y="0" width="0.5" height="1.0"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect04" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
|
||||
<rect x="0" y="0" width="0.5" height="1.0"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect05" clip-rule="evenodd">
|
||||
<rect x="0" y="60" width="10px" height="23px"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect06" clip-rule="evenodd">
|
||||
<rect x="10" y="60" width="10px" height="23px"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect4" clip-rule="evenodd">
|
||||
<rect x="200" y="200" width="200" height="200"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect-none" clip-rule="evenodd">
|
||||
</clipPath>
|
||||
<clipPath id="rect5" clip-rule="evenodd">
|
||||
<rect x="0" y="0" width="100" height="100"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect6" clip-rule="evenodd">
|
||||
<rect x="150" y="0" width="100" height="100"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect7" clip-rule="evenodd">
|
||||
<rect x="0" y="100" width="100" height="100"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect8" clip-rule="evenodd">
|
||||
<rect x="10" y="10" width="180" height="180"/>
|
||||
</clipPath>
|
||||
<clipPath id="rect9" clip-rule="evenodd">
|
||||
<rect x="100" y="100" width="200" height="200"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="circle1" clip-rule="evenodd">
|
||||
<circle cx="203" cy="203" r="150"/>
|
||||
</clipPath>
|
||||
<clipPath id="circle2" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
|
||||
<circle cx="0.5" cy="0.5" r="0.25"/>
|
||||
</clipPath>
|
||||
<clipPath id="circle3" clip-rule="evenodd">
|
||||
<circle cx="100" cy="100" r="50"/>
|
||||
<circle cx="300" cy="300" r="50"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="circle4" clip-rule="evenodd">
|
||||
<circle cx="50" cy="50" r="50"/>
|
||||
</clipPath>
|
||||
<clipPath id="circle5" clip-rule="evenodd">
|
||||
<circle cx="150" cy="50" r="50"/>
|
||||
</clipPath>
|
||||
<clipPath id="circle6" clip-rule="evenodd">
|
||||
<circle cx="50" cy="200" r="50"/>
|
||||
</clipPath>
|
||||
<clipPath id="circle7" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
|
||||
<circle cx="0.5" cy="0.5" r="0.5"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="circle8" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
|
||||
<circle cx="110" cy="20" r="90"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="circle9" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
|
||||
<circle cx="290" cy="20" r="90"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="circle10" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
|
||||
<circle cx="110" cy="200" r="90"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="circle11" clip-rule="evenodd">
|
||||
<circle cx="0" cy="0" r="150"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="star" clip-rule="evenodd">
|
||||
<path d="M400,25 L619,703 43,283 757,283 181,703 z" />
|
||||
</clipPath>
|
||||
|
||||
<marker id="m_atr" markerUnits="strokeWidth" markerWidth="3" markerHeight="3" viewBox="0 0 10 10" refX="5" refY="5">
|
||||
<polygon points="0,0 5,5 0,10 10,5" fill="red"/>
|
||||
</marker>
|
||||
|
||||
<switch>
|
||||
<rect id="rect-10" x="20" y="20" width="180" height="180" fill="blue" stroke="cyan" stroke-width="8"/>
|
||||
<rect id="rect-11" x="200" y="20" width="180" height="180" fill="lightgreen" stroke="none" />
|
||||
<rect id="rect-12" x="20" y="200" width="180" height="180" fill="darkcyan" stroke="none" />
|
||||
</switch>
|
||||
|
||||
<clipPath id="clipCircle1">
|
||||
<circle id="c1" cx="100" cy="100" r="50"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="clipCircle2">
|
||||
<circle id="c2" cx="150" cy="150" r="50"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="clipPath1">
|
||||
<path id="p1" d="M10 10l100 0 0 100 -100 0ZM50 50l40 0 0 40 -40 0Z" clip-rule="evenodd"/>
|
||||
</clipPath>
|
||||
|
||||
<!-- "If a valid 'clip-path' reference is placed on one of the children of a 'clipPath' element,
|
||||
then the given child element is clipped by the referenced clipping path before OR'ing the
|
||||
silhouette of the child element with the silhouettes of the other child elements." -->
|
||||
|
||||
<clipPath id="clipRects1">
|
||||
<rect x="50" y="30" width="25" height="100"/>
|
||||
<rect x="25" y="50" width="10" height="10" clip-path="url(#clipTwoCircles)"/>
|
||||
</clipPath>
|
||||
|
||||
<!-- Test use in a clipPath -->
|
||||
<clipPath id="clipTwoCircles">
|
||||
<use xlink:href="#c1"/>
|
||||
<use xlink:href="#c2"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="clipInClip1">
|
||||
<use xlink:href="#c2" clip-path="url(#clipCircle1)"/>
|
||||
<use xlink:href="#p1"/>
|
||||
</clipPath>
|
||||
|
||||
<clipPath id="clipOnClip1" clip-path="url(#clipCircle1)">
|
||||
<use xlink:href="#c2"/>
|
||||
<use xlink:href="#p1"/>
|
||||
</clipPath>
|
||||
|
||||
</defs>
|
||||
|
||||
<!-- text -->
|
||||
<text id="text1" font-size="20px" font-familiy="monospace" fill="red" x="0" y="50" clip-path="url('#rect01')">99</text>
|
||||
<text id="text2" font-size="20px" font-familiy="monospace" fill="blue" x="100" y="120" clip-path="url('#rect02')">99</text>
|
||||
<text id="text3" font-size="20px" font-familiy="monospace" clip-path="url('#rect03')" x="0" y="120">
|
||||
<tspan x="0" y="50" fill="red">99</tspan>
|
||||
</text>
|
||||
<text id="text4" font-size="20px" font-familiy="monospace" clip-path="url('#rect04')" x="0" y="120">
|
||||
<tspan x="100" y="120" fill="blue">99</tspan>
|
||||
</text>
|
||||
<text id="text5" font-size="20px" font-familiy="monospace" fill="red" x="0" y="80" clip-path="url('#rect05')">99</text>
|
||||
<text id="text6" font-size="20px" font-familiy="monospace" fill="blue" x="0" y="80" clip-path="url('#rect06')">99</text>
|
||||
|
||||
<!-- image -->
|
||||
<image id="image1" x="150" y="150" width="200" height="200" preserveApectRatio="none" clip="rect(200,300,300,200)"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image2" x="2" y="2" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image3" x="205" y="2" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image4" x="2" y="205" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image5" x="205" y="205" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image6" x="2" y="2" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image7" x="205" y="2" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image8" x="2" y="205" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image9" x="205" y="205" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image10" x="0" y="0" width="400" height="400" clip-path="url('#rect4')"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image11" x="0" y="0" width="400" height="400" clip-path="url('#rect-none')"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image12" x="25" y="43" width="768" height="768" clip-path="url('#star')" preserveApectRatio="none"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image13" x="0" y="0" width="400" height="400" clip-path="url('#circle3')"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<image id="image14" x="0" y="0" width="400" height="400" clip-path="url('#m_atr')"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
|
||||
|
||||
<!-- path -->
|
||||
<path id="path1" d="M10,50 L25,100 H110 V50 Q60,0 10,50" stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan" marker-mid="url(#m_atr)"/>
|
||||
<path id="path2" d="M160,50 L175,100 H260 V50 Q210,0 160,50" stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan" marker-mid="url(#m_atr)"/>
|
||||
<path id="path3" d="M10,150 L25,200 H110 V150 Q60,100 10,150" stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan" marker-mid="url(#m_atr)"/>
|
||||
|
||||
|
||||
<path id="path4" d="M10,50 L25,100 H110 V50 Q60,0 10,50" stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
|
||||
marker-mid="url(#m_atr)" clip-path="url(#circle4)"/>
|
||||
|
||||
<path id="path5" d="M160,50 L175,100 H260 V50 Q210,0 160,50" stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
|
||||
marker-mid="url(#m_atr)" clip-path="url(#circle5)"/>
|
||||
|
||||
<path id="path6" d="M10,150 L25,200 H110 V150 Q60,100 10,150" stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
|
||||
marker-mid="url(#m_atr)" clip-path="url(#circle6)"/>
|
||||
|
||||
<path id="path7" d="M10,50 L25,100 H110 V50 Q60,0 10,50"
|
||||
stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
|
||||
clip-path="url('#rect5')" marker-mid="url(#m_atr)"/>
|
||||
|
||||
<path id="path8" d="M160,50 L175,100 H260 V50 Q210,0 160,50"
|
||||
stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
|
||||
clip-path="url('#rect6')" marker-mid="url(#m_atr)"/>
|
||||
|
||||
<path id="path9" d="M10,150 L25,200 H110 V150 Q60,100 10,150"
|
||||
stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
|
||||
clip-path="url('#rect7')" marker-mid="url(#m_atr)"/>
|
||||
|
||||
<path id="path10" d="M10,50 L25,100 H110 V50 Q60,0 10,50"
|
||||
stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
|
||||
clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
|
||||
|
||||
<path id="path11" d="M160,50 L175,100 H260 V50 Q210,0 160,50"
|
||||
stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
|
||||
clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
|
||||
|
||||
<path id="path12" d="M10,150 L25,200 H110 V150 Q60,100 10,150"
|
||||
stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
|
||||
clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
|
||||
|
||||
<!-- use -->
|
||||
<use id="use1" xlink:href="#rect-10" x="50" y="50" clip-path="url('#circle8')"/>
|
||||
<use id="use2" xlink:href="#rect-11" x="50" y="50" clip-path="url('#circle9')"/>
|
||||
<use id="use3" xlink:href="#rect-12" x="50" y="50" clip-path="url('#circle10')"/>
|
||||
|
||||
<use id="use4" xlink:href="#rect-10" x="2" y="2" width="200" height="200" clip-path="url('#circle11')"/>
|
||||
<use id="use5" xlink:href="#rect-10" x="205" y="2" width="200" height="200" clip-path="url('#circle11')"/>
|
||||
<use id="use6" xlink:href="#rect-10" x="2" y="205" width="200" height="200" clip-path="url('#circle11')"/>
|
||||
<use id="use7" xlink:href="#rect-10" x="205" y="205" width="200" height="200" clip-path="url('#circle11')"/>
|
||||
|
||||
<use id="use8" xlink:href="#rect-10" x="50" y="50" clip-path="url('#m_atr')"/>
|
||||
|
||||
<!-- foreignObject -->
|
||||
<foreignObject id="fo1" x="2" y="2" width="200" height="200" clip-path="url('#circle1')" clip="rect(2,102,102,2)">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
|
||||
There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
|
||||
</div>
|
||||
</foreignObject>
|
||||
<foreignObject id="fo2" x="205" y="2" width="200" height="200" clip-path="url('#circle1')" >
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
|
||||
There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
|
||||
</div>
|
||||
</foreignObject>
|
||||
<foreignObject id="fo3" x="2" y="205" width="200" height="200" clip-path="url('#circle1')" >
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
|
||||
There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
|
||||
</div>
|
||||
</foreignObject>
|
||||
<foreignObject id="fo4" x="205" y="205" width="200" height="200" clip-path="url('#circle1')" clip="rect(2,102,102,2)">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
|
||||
There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
|
||||
</div>
|
||||
</foreignObject>
|
||||
|
||||
<foreignObject id="fo5" x="250" y="250" width="200" height="200" clip-path="url('#rect8')">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
|
||||
There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
|
||||
</div>
|
||||
</foreignObject>
|
||||
|
||||
<foreignObject id="fo6" x="0" y="0" width="200" height="200" clip-path="url('#rect9')">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
|
||||
There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
|
||||
</div>
|
||||
</foreignObject>
|
||||
|
||||
<foreignObject id="fo7" x="0" y="0" width="200" height="200" clip-path="url('#rect8')">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
|
||||
There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
|
||||
</div>
|
||||
</foreignObject>
|
||||
|
||||
<foreignObject id="fo8" x="0" y="0" width="200" height="200" clip-path="url('#m_atr')">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
|
||||
There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
|
||||
</div>
|
||||
</foreignObject>
|
||||
|
||||
<!-- -->
|
||||
<rect id="rect-1" width="200" height="200" fill="blue" clip-path="url(#clipInClip1)"/>
|
||||
<rect id="rect-2" width="200" height="200" fill="blue" clip-path="url(#clipRects1)"/>
|
||||
<rect id="rect-3" width="300" height="300" fill="blue" clip-path="url(#clipOnClip1)"/>
|
||||
|
||||
<g clip-path="url(#clipCircle1)" id="g1">
|
||||
<use xlink:href="#c2" fill="red"/>
|
||||
<use xlink:href="#p1" fill="red" fill-rule="evenodd"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 20 KiB |
@ -9,6 +9,7 @@ support-files =
|
||||
animated-svg-image-helper.svg
|
||||
bbox-helper.svg
|
||||
bounds-helper.svg
|
||||
getBBox-method-helper.svg
|
||||
dataTypes-helper.svg
|
||||
fragments-helper.svg
|
||||
getCTM-helper.svg
|
||||
@ -34,6 +35,7 @@ support-files =
|
||||
[test_bbox.xhtml]
|
||||
[test_bounds.html]
|
||||
[test_bug872812.html]
|
||||
[test_getBBox-method.html]
|
||||
[test_dataTypes.html]
|
||||
[test_dataTypesModEvents.html]
|
||||
[test_fragments.html]
|
||||
|
249
content/svg/content/test/test_getBBox-method.html
Normal file
249
content/svg/content/test/test_getBBox-method.html
Normal file
@ -0,0 +1,249 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=999964
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Test case for Bug 999964</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999964">Mozilla Bug 999964</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
|
||||
<iframe id="svg" src="getBBox-method-helper.svg"></iframe>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript">
|
||||
|
||||
/** Test case for Bug 999964 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function run()
|
||||
{
|
||||
var flag = SpecialPowers.getBoolPref("svg.new-getBBox.enabled");
|
||||
if (!flag) {
|
||||
ok(!flag, "skip test for bug999964.");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var doc = $("svg").contentDocument;
|
||||
|
||||
function isFuzzy(a, b, error, name)
|
||||
{
|
||||
ok(!(Math.abs(a - b) > error), name, "got " + a + ", expected " + b + " (within " + error + ")");
|
||||
}
|
||||
|
||||
function getBBox(id, opt) {
|
||||
return doc.getElementById(id).getBBox(opt);
|
||||
}
|
||||
|
||||
function checkBBox(id, opt, x, y, width, height, error) {
|
||||
var bbox = getBBox(id, opt);
|
||||
isFuzzy(bbox.x, x, error, id + ".getBBox().x");
|
||||
isFuzzy(bbox.y, y, error, id + ".getBBox().y");
|
||||
isFuzzy(bbox.width, width, error, id + ".getBBox().width");
|
||||
isFuzzy(bbox.height, height, error, id + ".getBBox().height");
|
||||
}
|
||||
|
||||
function compareBBox1(id1, id2) {
|
||||
var bbox1 = getBBox(id1);
|
||||
var bbox2 = getBBox(id2);
|
||||
is(bbox1.x, bbox2.x, id1 + ".getBBox().x");
|
||||
is(bbox1.y, bbox2.y, id1 + ".getBBox().y");
|
||||
isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width");
|
||||
isFuzzy(bbox1.height, bbox2.height, 0.0001, id1 + ".getBBox().height");
|
||||
}
|
||||
|
||||
function compareBBox2(id1, id2) {
|
||||
// without 'x'
|
||||
var bbox1 = getBBox(id1);
|
||||
var bbox2 = getBBox(id2);
|
||||
is(bbox1.y, bbox2.y, id1 + ".getBBox().y");
|
||||
isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width");
|
||||
isFuzzy(bbox1.height, bbox2.height, 0.0001, id1 + ".getBBox().height");
|
||||
}
|
||||
|
||||
var opt = { fill: true, stroke: true, markers: true, clipped: true };
|
||||
|
||||
// <text>
|
||||
// fill
|
||||
opt = { fill: true, stroke: false, markers: false, clipped: false };
|
||||
compareBBox1("text1","text3");
|
||||
compareBBox1("text2","text4");
|
||||
compareBBox1("text5","text6");
|
||||
// all
|
||||
opt = { fill: true, stroke: true, markers: true, clipped: true };
|
||||
compareBBox2("text1","text3");
|
||||
compareBBox2("text2","text4");
|
||||
compareBBox2("text5","text6");
|
||||
// clipped
|
||||
opt = { fill: false, stroke: false, markers: false, clipped: true };
|
||||
compareBBox2("text1","text3");
|
||||
compareBBox2("text2","text4");
|
||||
compareBBox2("text5","text6");
|
||||
|
||||
// <image>
|
||||
opt = { fill: true, stroke: true, markers: true, clipped: true };
|
||||
checkBBox("image1", opt, 250, 250, 100, 100);
|
||||
checkBBox("image2", opt, 53, 53, 149, 149);
|
||||
checkBBox("image3", opt, 205, 53, 148, 149);
|
||||
checkBBox("image4", opt, 53, 205, 149, 148);
|
||||
checkBBox("image5", opt, 205, 205, 148, 148);
|
||||
checkBBox("image6", opt, 52, 52, 100, 100);
|
||||
checkBBox("image7", opt, 255, 52, 100, 100);
|
||||
checkBBox("image8", opt, 52, 255, 100, 100);
|
||||
checkBBox("image9", opt, 255, 255, 100, 100);
|
||||
checkBBox("image10", opt, 200, 200, 200, 200);
|
||||
checkBBox("image11", opt, 0, 0, 0, 0);
|
||||
checkBBox("image12", opt, 43, 43, 714, 660);
|
||||
checkBBox("image13", opt, 50, 50, 300, 300);
|
||||
checkBBox("image14", opt, 0, 0, 0, 0);
|
||||
|
||||
opt = { fill: true, stroke: false, markers: false, clipped: false };
|
||||
checkBBox("image1", opt, 150,150,200,200, 0);
|
||||
checkBBox("image2", opt, 2,2,200,200, 0);
|
||||
checkBBox("image3", opt, 205,2,200,200, 0);
|
||||
checkBBox("image4", opt, 2,205,200,200, 0);
|
||||
checkBBox("image5", opt, 205,205,200,200, 0);
|
||||
checkBBox("image6", opt, 2,2,200,200, 0);
|
||||
checkBBox("image7", opt, 205,2,200,200, 0);
|
||||
checkBBox("image8", opt, 2,205,200,200, 0);
|
||||
checkBBox("image9", opt, 205,205,200,200, 0);
|
||||
checkBBox("image10", opt, 0,0,400,400, 0);
|
||||
checkBBox("image11", opt, 0,0,400,400, 0);
|
||||
checkBBox("image12", opt, 25,43,768,768, 0);
|
||||
checkBBox("image13", opt, 0,0,400,400, 0);
|
||||
|
||||
// <path>
|
||||
opt = { fill: true, stroke: true, markers: true, clipped: true };
|
||||
checkBBox("path1", opt, 2,17,120,95, 0);
|
||||
checkBBox("path2", opt, 156,21,116,91, 0);
|
||||
checkBBox("path3", opt, 6,121,116,91, 0);
|
||||
checkBBox("path4", opt, 2,17,98,83, 0);
|
||||
checkBBox("path5", opt, 156,21,44,79, 0);
|
||||
checkBBox("path6", opt, 6,150,94,62, 0);
|
||||
checkBBox("path7", opt, 2,17,98,83, 0);
|
||||
checkBBox("path8", opt, 156,21,94,79, 0);
|
||||
checkBBox("path9", opt, 6,121,94,79, 0);
|
||||
checkBBox("path10", opt, 10,25,100,75, 0);
|
||||
checkBBox("path11", opt, 160,25,100,75, 0);
|
||||
checkBBox("path12", opt, 10,125,100,75, 0);
|
||||
|
||||
opt = { fill: true, stroke: false, markers: false, clipped: true };
|
||||
checkBBox("path1", opt, 10,25,100,75, 0);
|
||||
checkBBox("path2", opt, 160,25,100,75, 0);
|
||||
checkBBox("path3", opt, 10,125,100,75, 0);
|
||||
checkBBox("path4", opt, 10,25,90,75, 0);
|
||||
checkBBox("path5", opt, 160,25,40,75, 0);
|
||||
checkBBox("path6", opt, 10,150,90,50, 0);
|
||||
checkBBox("path7", opt, 10,25,90,75, 0);
|
||||
checkBBox("path8", opt, 160,25,90,75, 0);
|
||||
checkBBox("path9", opt, 10,125,90,75, 0);
|
||||
checkBBox("path10", opt, 10,25,100,75, 0);
|
||||
checkBBox("path11", opt, 160,25,100,75, 0);
|
||||
checkBBox("path12", opt, 10,125,100,75, 0);
|
||||
|
||||
opt = { fill: true, stroke: false, markers: false, clipped: false };
|
||||
checkBBox("path1", opt, 10,25,100,75, 0);
|
||||
checkBBox("path2", opt, 160,25,100,75, 0);
|
||||
checkBBox("path3", opt, 10,125,100,75, 0);
|
||||
checkBBox("path4", opt, 10,25,100,75, 0);
|
||||
checkBBox("path5", opt, 160,25,100,75, 0);
|
||||
checkBBox("path6", opt, 10,125,100,75, 0);
|
||||
checkBBox("path7", opt, 10,25,100,75, 0);
|
||||
checkBBox("path8", opt, 160,25,100,75, 0);
|
||||
checkBBox("path9", opt, 10,125,100,75, 0);
|
||||
checkBBox("path10", opt, 10,25,100,75, 0);
|
||||
checkBBox("path11", opt, 160,25,100,75, 0);
|
||||
checkBBox("path12", opt, 10,125,100,75, 0);
|
||||
|
||||
opt = { fill: false, stroke: true, markers: false, clipped: false };
|
||||
checkBBox("path1", opt, 2,17,116,91, 0);
|
||||
checkBBox("path2", opt, 156,21,108,83, 0);
|
||||
checkBBox("path3", opt, 6,121,108,83, 0);
|
||||
checkBBox("path4", opt, 2,17,116,91, 0);
|
||||
checkBBox("path5", opt, 156,21,108,83, 0);
|
||||
checkBBox("path6", opt, 6,121,108,83, 0);
|
||||
checkBBox("path7", opt, 2,17,116,91, 0);
|
||||
checkBBox("path8", opt, 156,21,108,83, 0);
|
||||
checkBBox("path9", opt, 6,121,108,83, 0);
|
||||
checkBBox("path10", opt, 2,17,116,91, 0);
|
||||
checkBBox("path11", opt, 156,21,108,83, 0);
|
||||
checkBBox("path12", opt, 6,121,108,83, 0);
|
||||
|
||||
opt = { fill: false, stroke: false, markers: true, clipped: false };
|
||||
checkBBox("path1", opt, 10,25,112,87, 0);
|
||||
checkBBox("path2", opt, 160,25,112,87, 0);
|
||||
checkBBox("path3", opt, 10,125,112,87, 0);
|
||||
checkBBox("path4", opt, 10,25,112,87, 0);
|
||||
checkBBox("path5", opt, 160,25,112,87, 0);
|
||||
checkBBox("path6", opt, 10,125,112,87, 0);
|
||||
checkBBox("path7", opt, 10,25,112,87, 0);
|
||||
checkBBox("path8", opt, 160,25,112,87, 0);
|
||||
checkBBox("path9", opt, 10,125,112,87, 0);
|
||||
checkBBox("path10", opt, 10,25,112,87, 0);
|
||||
checkBBox("path11", opt, 160,25,112,87, 0);
|
||||
checkBBox("path12", opt, 10,125,112,87, 0);
|
||||
|
||||
// <use>
|
||||
opt = { fill: true, stroke: false, markers: false, clipped: false };
|
||||
checkBBox("use1", opt, 70,70,180,180, 0);
|
||||
checkBBox("use2", opt, 250,70,180,180, 0);
|
||||
checkBBox("use3", opt, 70,250,180,180, 0);
|
||||
checkBBox("use4", opt, 22,22,180,180, 0);
|
||||
checkBBox("use5", opt, 225,22,180,180, 0);
|
||||
checkBBox("use6", opt, 22,225,180,180, 0);
|
||||
checkBBox("use7", opt, 225,225,180,180, 0);
|
||||
|
||||
opt = { fill: true, stroke: true, markers: true, clipped: true };
|
||||
checkBBox("use1", opt, 70,66,180,94, 0);
|
||||
checkBBox("use2", opt, 250,70,180,90, 0);
|
||||
checkBBox("use3", opt, 70,250,180,90, 0);
|
||||
checkBBox("use4", opt, 18,18,134,134, 0);
|
||||
checkBBox("use5", opt, 221,18,134,134, 0);
|
||||
checkBBox("use6", opt, 18,221,134,134, 0);
|
||||
checkBBox("use7", opt, 221,221,134,134, 0);
|
||||
checkBBox("use8", opt, 0,0,0,0, 0);
|
||||
|
||||
// <foreignObject>
|
||||
opt = { fill: true, stroke: false, markers: false, clipped: false };
|
||||
checkBBox("fo1", opt, 2,2,200,200, 0);
|
||||
checkBBox("fo2", opt, 205,2,200,200, 0);
|
||||
checkBBox("fo3", opt, 2,205,200,200, 0);
|
||||
checkBBox("fo4", opt, 205,205,200,200, 0);
|
||||
checkBBox("fo5", opt, 250,250,200,200, 0);
|
||||
checkBBox("fo6", opt, 0,0,200,200, 0);
|
||||
checkBBox("fo7", opt, 0,0,200,200, 0);
|
||||
|
||||
opt = { fill: true, stroke: true, markers: true, clipped: true };
|
||||
checkBBox("fo1", opt, 53,53,51,51, 0);
|
||||
checkBBox("fo2", opt, 205,53,148,149, 0);
|
||||
checkBBox("fo3", opt, 53,205,149,148, 0);
|
||||
checkBBox("fo4", opt, 207,207,100,100, 0);
|
||||
checkBBox("fo5", opt, 0,0,0,0, 0);
|
||||
checkBBox("fo6", opt, 100,100,100,100, 0);
|
||||
checkBBox("fo7", opt, 10,10,180,180, 0);
|
||||
checkBBox("fo8", opt, 0,0,0,0, 0);
|
||||
|
||||
// from http://www.w3.org/Graphics/SVG/Test/20110816/harness/htmlObjectApproved/masking-path-07-b.html
|
||||
opt = { fill: true, stroke: true, markers: true, clipped: true };
|
||||
checkBBox("rect-1", opt, 10,10,140,140, 0);
|
||||
checkBBox("rect-2", opt, 50,30,25,100, 0);
|
||||
checkBBox("rect-3", opt, 50,50,100,100, 0);
|
||||
checkBBox("g1", opt, 50,50,100,100, 0);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("load", run, false);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -344,10 +344,6 @@ let Activities = {
|
||||
msg.forEach(function(aActivity) {
|
||||
self.updateContentTypeList(aActivity, res);
|
||||
});
|
||||
if (res.length) {
|
||||
ppmm.broadcastAsyncMessage("Activities:RegisterContentTypes",
|
||||
{ contentTypes: res });
|
||||
}
|
||||
},
|
||||
function onError(aEvent) {
|
||||
msg.error = "REGISTER_ERROR";
|
||||
@ -360,13 +356,6 @@ let Activities = {
|
||||
msg.forEach(function(aActivity) {
|
||||
this.updateContentTypeList(aActivity, res);
|
||||
}, this);
|
||||
if (res.length) {
|
||||
ppmm.broadcastAsyncMessage("Activities:UnregisterContentTypes",
|
||||
{ contentTypes: res });
|
||||
}
|
||||
break;
|
||||
case "Activities:GetContentTypes":
|
||||
this.sendContentTypes(mm);
|
||||
break;
|
||||
case "child-process-shutdown":
|
||||
for (let id in this.callers) {
|
||||
@ -404,24 +393,6 @@ let Activities = {
|
||||
aResult.push(contentType);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
sendContentTypes: function sendContentTypes(aMm) {
|
||||
let res = [];
|
||||
let self = this;
|
||||
this.db.find({ options: { name: "view" } },
|
||||
function() { // Success callback.
|
||||
if (res.length) {
|
||||
aMm.sendAsyncMessage("Activities:RegisterContentTypes",
|
||||
{ contentTypes: res });
|
||||
}
|
||||
},
|
||||
null, // Error callback.
|
||||
function(aActivity) { // Matching callback.
|
||||
self.updateContentTypeList(aActivity, res)
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,6 +98,11 @@
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
#include "mozilla/Hal.h"
|
||||
#endif
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -1483,41 +1488,27 @@ Navigator::GetFeature(const nsAString& aName)
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
if (aName.EqualsLiteral("hardware.memory")) {
|
||||
static int memLevel = 1;
|
||||
if (memLevel == 1) {
|
||||
FILE* f = fopen("/proc/meminfo", "r");
|
||||
if (!f) {
|
||||
p->MaybeReject(NS_LITERAL_STRING("CannotOpenMeminfo"));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
int memTotal;
|
||||
int n = fscanf(f, "MemTotal: %d kB\n", &memTotal);
|
||||
fclose(f);
|
||||
|
||||
if (memTotal == 0 || n != 1) {
|
||||
// with seccomp enabled, fopen() should be in a non-sandboxed process
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
uint32_t memLevel = mozilla::hal::GetTotalSystemMemoryLevel();
|
||||
if (memLevel == 0) {
|
||||
p->MaybeReject(NS_LITERAL_STRING("Abnormal"));
|
||||
return p.forget();
|
||||
}
|
||||
// From KB to MB
|
||||
memTotal /= 1024;
|
||||
|
||||
// round the value up to the next power of two
|
||||
while (memLevel <= memTotal) {
|
||||
memLevel *= 2;
|
||||
}
|
||||
p->MaybeResolve((int)memLevel);
|
||||
} else {
|
||||
mozilla::dom::ContentChild* cc =
|
||||
mozilla::dom::ContentChild::GetSingleton();
|
||||
nsRefPtr<Promise> ipcRef(p);
|
||||
cc->SendGetSystemMemory(reinterpret_cast<uint64_t>(ipcRef.forget().take()));
|
||||
}
|
||||
p->MaybeResolve(memLevel);
|
||||
return p.forget();
|
||||
} // hardware.memory
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// resolve with <undefined> because the feature name is not supported
|
||||
p->MaybeResolve(JS::UndefinedHandleValue);
|
||||
}
|
||||
// resolve with <undefined> because the feature name is not supported
|
||||
p->MaybeResolve(JS::UndefinedHandleValue);
|
||||
|
||||
return p.forget();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
21
dom/base/SlowScriptDebug.js
Normal file
21
dom/base/SlowScriptDebug.js
Normal file
@ -0,0 +1,21 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function SlowScriptDebug() { }
|
||||
|
||||
SlowScriptDebug.prototype = {
|
||||
classID: Components.ID("{e740ddb4-18b4-4aac-8ae1-9b0f4320769d}"),
|
||||
classDescription: "Slow script debug handler",
|
||||
contractID: "@mozilla.org/dom/slow-script-debug;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISlowScriptDebug]),
|
||||
|
||||
get activationHandler() { return this._activationHandler; },
|
||||
set activationHandler(cb) { return this._activationHandler = cb; },
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SlowScriptDebug]);
|
2
dom/base/SlowScriptDebug.manifest
Normal file
2
dom/base/SlowScriptDebug.manifest
Normal file
@ -0,0 +1,2 @@
|
||||
component {e740ddb4-18b4-4aac-8ae1-9b0f4320769d} SlowScriptDebug.js
|
||||
contract @mozilla.org/dom/slow-script-debug;1 {e740ddb4-18b4-4aac-8ae1-9b0f4320769d}
|
@ -13,6 +13,7 @@ XPIDL_SOURCES += [
|
||||
'nsIEntropyCollector.idl',
|
||||
'nsIScriptChannel.idl',
|
||||
'nsISiteSpecificUserAgent.idl',
|
||||
'nsISlowScriptDebug.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom'
|
||||
@ -128,6 +129,8 @@ EXTRA_COMPONENTS += [
|
||||
'ConsoleAPIStorage.js',
|
||||
'SiteSpecificUserAgent.js',
|
||||
'SiteSpecificUserAgent.manifest',
|
||||
'SlowScriptDebug.js',
|
||||
'SlowScriptDebug.manifest',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
|
@ -485,10 +485,6 @@ bool nsDOMClassInfo::sIsInitialized = false;
|
||||
|
||||
jsid nsDOMClassInfo::sLocation_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sConstructor_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sLength_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sItem_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sNamedItem_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sEnumerate_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sTop_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sDocument_id = JSID_VOID;
|
||||
jsid nsDOMClassInfo::sWrappedJSObject_id = JSID_VOID;
|
||||
@ -595,10 +591,6 @@ nsDOMClassInfo::DefineStaticJSVals(JSContext *cx)
|
||||
|
||||
SET_JSID_TO_STRING(sLocation_id, cx, "location");
|
||||
SET_JSID_TO_STRING(sConstructor_id, cx, "constructor");
|
||||
SET_JSID_TO_STRING(sLength_id, cx, "length");
|
||||
SET_JSID_TO_STRING(sItem_id, cx, "item");
|
||||
SET_JSID_TO_STRING(sNamedItem_id, cx, "namedItem");
|
||||
SET_JSID_TO_STRING(sEnumerate_id, cx, "enumerateProperties");
|
||||
SET_JSID_TO_STRING(sTop_id, cx, "top");
|
||||
SET_JSID_TO_STRING(sDocument_id, cx, "document");
|
||||
SET_JSID_TO_STRING(sWrappedJSObject_id, cx, "wrappedJSObject");
|
||||
@ -1717,9 +1709,6 @@ nsDOMClassInfo::ShutDown()
|
||||
|
||||
sLocation_id = JSID_VOID;
|
||||
sConstructor_id = JSID_VOID;
|
||||
sLength_id = JSID_VOID;
|
||||
sItem_id = JSID_VOID;
|
||||
sEnumerate_id = JSID_VOID;
|
||||
sTop_id = JSID_VOID;
|
||||
sDocument_id = JSID_VOID;
|
||||
sWrappedJSObject_id = JSID_VOID;
|
||||
|
@ -166,10 +166,6 @@ protected:
|
||||
public:
|
||||
static jsid sLocation_id;
|
||||
static jsid sConstructor_id;
|
||||
static jsid sLength_id;
|
||||
static jsid sItem_id;
|
||||
static jsid sNamedItem_id;
|
||||
static jsid sEnumerate_id;
|
||||
static jsid sTop_id;
|
||||
static jsid sDocument_id;
|
||||
static jsid sWrappedJSObject_id;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "nsIScriptTimeoutHandler.h"
|
||||
#include "nsIController.h"
|
||||
#include "nsScriptNameSpaceManager.h"
|
||||
#include "nsISlowScriptDebug.h"
|
||||
#include "nsWindowMemoryReporter.h"
|
||||
#include "WindowNamedPropertiesHandler.h"
|
||||
|
||||
@ -1344,12 +1345,14 @@ nsGlobalWindow::~nsGlobalWindow()
|
||||
void
|
||||
nsGlobalWindow::AddEventTargetObject(DOMEventTargetHelper* aObject)
|
||||
{
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
mEventTargetObjects.PutEntry(aObject);
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
|
||||
{
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
mEventTargetObjects.RemoveEntry(aObject);
|
||||
}
|
||||
|
||||
@ -1986,8 +1989,10 @@ nsGlobalWindow::OuterObject(JSContext* aCx, JS::Handle<JSObject*> aObj)
|
||||
}
|
||||
|
||||
bool
|
||||
nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
|
||||
nsGlobalWindow::WouldReuseInnerWindow(nsIDocument* aNewDocument)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
// We reuse the inner window when:
|
||||
// a. We are currently at our original document.
|
||||
// b. At least one of the following conditions are true:
|
||||
@ -2027,7 +2032,7 @@ nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
|
||||
void
|
||||
nsGlobalWindow::SetInitialPrincipalToSubject()
|
||||
{
|
||||
FORWARD_TO_OUTER_VOID(SetInitialPrincipalToSubject, ());
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
// First, grab the subject principal.
|
||||
nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::SubjectPrincipal();
|
||||
@ -2296,11 +2301,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
|
||||
nsCOMPtr<nsIDocument> oldDoc = mDoc;
|
||||
|
||||
nsIScriptContext *scx = GetContextInternal();
|
||||
NS_ENSURE_TRUE(scx, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
JSContext *cx = scx->GetNativeContext();
|
||||
|
||||
#ifndef MOZ_DISABLE_CRYPTOLEGACY
|
||||
// clear smartcard events, our document has gone away.
|
||||
if (mCrypto && XRE_GetProcessType() != GeckoProcessType_Content) {
|
||||
@ -2309,6 +2309,9 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
}
|
||||
#endif
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
JSContext *cx = jsapi.cx();
|
||||
|
||||
if (!mDoc) {
|
||||
// First document load.
|
||||
|
||||
@ -2365,9 +2368,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
|
||||
bool thisChrome = IsChromeWindow();
|
||||
|
||||
nsCxPusher cxPusher;
|
||||
cxPusher.Push(cx);
|
||||
|
||||
// Check if we're near the stack limit before we get anywhere near the
|
||||
// transplanting code.
|
||||
JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE);
|
||||
@ -2660,8 +2660,11 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
// We wait to fire the debugger hook until the window is all set up and hooked
|
||||
// up with the outer. See bug 969156.
|
||||
if (createdInnerWindow) {
|
||||
JS::Rooted<JSObject*> global(cx, newInnerWindow->GetWrapper());
|
||||
JS_FireOnNewGlobalObject(cx, global);
|
||||
// AutoEntryScript required to invoke debugger hook, which is a
|
||||
// Gecko-specific concept at present.
|
||||
AutoEntryScript aes(newInnerWindow);
|
||||
JS::Rooted<JSObject*> global(aes.cx(), newInnerWindow->GetWrapper());
|
||||
JS_FireOnNewGlobalObject(aes.cx(), global);
|
||||
}
|
||||
|
||||
if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
|
||||
@ -2686,6 +2689,8 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
void
|
||||
nsGlobalWindow::PreloadLocalStorage()
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
if (!Preferences::GetBool(kStorageEnabled)) {
|
||||
return;
|
||||
}
|
||||
@ -2713,6 +2718,8 @@ nsGlobalWindow::PreloadLocalStorage()
|
||||
void
|
||||
nsGlobalWindow::DispatchDOMWindowCreated()
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
if (!mDoc) {
|
||||
return;
|
||||
}
|
||||
@ -3063,6 +3070,8 @@ nsGlobalWindow::ShouldPromptToBlockDialogs()
|
||||
bool
|
||||
nsGlobalWindow::AreDialogsEnabled()
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
nsGlobalWindow *topWindow = GetScriptableTop();
|
||||
if (!topWindow) {
|
||||
NS_ERROR("AreDialogsEnabled() called without a top window?");
|
||||
@ -6263,6 +6272,12 @@ nsGlobalWindow::AlertOrConfirm(bool aAlert,
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::Alert(mozilla::ErrorResult& aError)
|
||||
{
|
||||
Alert(EmptyString(), aError);
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::Alert(const nsAString& aMessage, mozilla::ErrorResult& aError)
|
||||
{
|
||||
@ -8216,8 +8231,11 @@ public:
|
||||
bool
|
||||
nsGlobalWindow::CanClose()
|
||||
{
|
||||
if (!mDocShell)
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
if (!mDocShell) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ask the content viewer whether the toplevel window can close.
|
||||
// If the content viewer returns false, it is responsible for calling
|
||||
@ -8311,7 +8329,7 @@ nsGlobalWindow::Close(ErrorResult& aError)
|
||||
return;
|
||||
}
|
||||
|
||||
aError = FinalClose();
|
||||
FinalClose();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -8323,32 +8341,35 @@ nsGlobalWindow::Close()
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
nsGlobalWindow::ForceClose()
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
if (IsFrame() || !mDocShell) {
|
||||
// This may be a frame in a frameset, or a window that's already closed.
|
||||
// Ignore such calls.
|
||||
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mHavePendingClose) {
|
||||
// We're going to be closed anyway; do nothing since we don't want
|
||||
// to double-close
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
mInClose = true;
|
||||
|
||||
DispatchCustomEvent("DOMWindowClose");
|
||||
|
||||
return FinalClose();
|
||||
FinalClose();
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
nsGlobalWindow::FinalClose()
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
// Flag that we were closed.
|
||||
mIsClosed = true;
|
||||
|
||||
@ -8372,8 +8393,6 @@ nsGlobalWindow::FinalClose()
|
||||
} else {
|
||||
mHavePendingClose = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -10749,27 +10768,43 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
||||
unsigned lineno;
|
||||
bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno);
|
||||
|
||||
bool debugPossible = hasFrame && js::CanCallContextDebugHandler(cx);
|
||||
#ifdef MOZ_JSDEBUGGER
|
||||
// Get the debugger service if necessary.
|
||||
if (debugPossible) {
|
||||
bool jsds_IsOn = false;
|
||||
const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
|
||||
nsCOMPtr<jsdIExecutionHook> jsdHook;
|
||||
nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
|
||||
// Prioritize the SlowScriptDebug interface over JSD1.
|
||||
nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
|
||||
bool oldDebugPossible = false;
|
||||
|
||||
// Check if there's a user for the debugger service that's 'on' for us
|
||||
if (hasFrame) {
|
||||
const char *debugCID = "@mozilla.org/dom/slow-script-debug;1";
|
||||
nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
|
||||
jsds->GetIsOn(&jsds_IsOn);
|
||||
debugService->GetActivationHandler(getter_AddRefs(debugCallback));
|
||||
}
|
||||
|
||||
// If there is a debug handler registered for this runtime AND
|
||||
// ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
|
||||
// then something useful will be done with our request to debug.
|
||||
debugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn);
|
||||
}
|
||||
if (!debugCallback) {
|
||||
oldDebugPossible = js::CanCallContextDebugHandler(cx);
|
||||
#ifdef MOZ_JSDEBUGGER
|
||||
// Get the debugger service if necessary.
|
||||
if (oldDebugPossible) {
|
||||
bool jsds_IsOn = false;
|
||||
const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
|
||||
nsCOMPtr<jsdIExecutionHook> jsdHook;
|
||||
nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
|
||||
|
||||
// Check if there's a user for the debugger service that's 'on' for us
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
|
||||
jsds->GetIsOn(&jsds_IsOn);
|
||||
}
|
||||
|
||||
// If there is a debug handler registered for this runtime AND
|
||||
// ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
|
||||
// then something useful will be done with our request to debug.
|
||||
oldDebugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool showDebugButton = debugCallback || oldDebugPossible;
|
||||
|
||||
// Get localizable strings
|
||||
nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
|
||||
@ -10800,7 +10835,7 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
||||
}
|
||||
|
||||
|
||||
if (debugPossible) {
|
||||
if (showDebugButton) {
|
||||
tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
||||
"DebugScriptButton",
|
||||
debugButton);
|
||||
@ -10826,7 +10861,7 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
||||
|
||||
// GetStringFromName can return NS_OK and still give nullptr string
|
||||
if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
|
||||
(!debugButton && debugPossible) || !neverShowDlg) {
|
||||
(!debugButton && showDebugButton) || !neverShowDlg) {
|
||||
NS_ERROR("Failed to get localized strings.");
|
||||
return ContinueSlowScript;
|
||||
}
|
||||
@ -10856,7 +10891,7 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
||||
(nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
|
||||
|
||||
// Add a third button if necessary.
|
||||
if (debugPossible)
|
||||
if (showDebugButton)
|
||||
buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
|
||||
|
||||
// Null out the operation callback while we're re-entering JS here.
|
||||
@ -10873,8 +10908,15 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
||||
if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
|
||||
return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;
|
||||
}
|
||||
if ((buttonPressed == 2) && debugPossible) {
|
||||
return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript;
|
||||
if (buttonPressed == 2) {
|
||||
if (debugCallback) {
|
||||
rv = debugCallback->HandleSlowScriptDebug(this);
|
||||
return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
|
||||
}
|
||||
|
||||
if (oldDebugPossible) {
|
||||
return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript;
|
||||
}
|
||||
}
|
||||
JS_ClearPendingException(cx);
|
||||
return KillSlowScript;
|
||||
|
@ -434,6 +434,7 @@ public:
|
||||
virtual NS_HIDDEN_(void) SetIsBackground(bool aIsBackground);
|
||||
virtual NS_HIDDEN_(void) SetChromeEventHandler(mozilla::dom::EventTarget* aChromeEventHandler);
|
||||
|
||||
// Outer windows only.
|
||||
virtual NS_HIDDEN_(void) SetInitialPrincipalToSubject();
|
||||
|
||||
virtual NS_HIDDEN_(PopupControlState) PushPopupControlState(PopupControlState state, bool aForce) const;
|
||||
@ -453,14 +454,18 @@ public:
|
||||
}
|
||||
virtual NS_HIDDEN_(bool) IsRunningTimeout() { return mTimeoutFiringDepth > 0; }
|
||||
|
||||
virtual NS_HIDDEN_(bool) WouldReuseInnerWindow(nsIDocument *aNewDocument);
|
||||
// Outer windows only.
|
||||
virtual NS_HIDDEN_(bool) WouldReuseInnerWindow(nsIDocument* aNewDocument);
|
||||
|
||||
virtual NS_HIDDEN_(void) SetDocShell(nsIDocShell* aDocShell);
|
||||
virtual void DetachFromDocShell();
|
||||
virtual NS_HIDDEN_(nsresult) SetNewDocument(nsIDocument *aDocument,
|
||||
nsISupports *aState,
|
||||
bool aForceReuseInnerWindow);
|
||||
|
||||
// Outer windows only.
|
||||
void DispatchDOMWindowCreated();
|
||||
|
||||
virtual NS_HIDDEN_(void) SetOpenerWindow(nsIDOMWindow* aOpener,
|
||||
bool aOriginalOpener);
|
||||
|
||||
@ -470,8 +475,9 @@ public:
|
||||
virtual NS_HIDDEN_(void) EnterModalState();
|
||||
virtual NS_HIDDEN_(void) LeaveModalState();
|
||||
|
||||
// Outer windows only.
|
||||
virtual NS_HIDDEN_(bool) CanClose();
|
||||
virtual NS_HIDDEN_(nsresult) ForceClose();
|
||||
virtual NS_HIDDEN_(void) ForceClose();
|
||||
|
||||
virtual NS_HIDDEN_(void) MaybeUpdateTouchState();
|
||||
virtual NS_HIDDEN_(void) UpdateTouchState();
|
||||
@ -557,6 +563,7 @@ public:
|
||||
// (alert, prompt, confirm) are currently allowed in this window.
|
||||
void EnableDialogs();
|
||||
void DisableDialogs();
|
||||
// Outer windows only.
|
||||
bool AreDialogsEnabled();
|
||||
|
||||
nsIScriptContext *GetContextInternal()
|
||||
@ -696,6 +703,7 @@ public:
|
||||
|
||||
void UnmarkGrayTimers();
|
||||
|
||||
// Inner windows only.
|
||||
void AddEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
|
||||
void RemoveEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
|
||||
|
||||
@ -848,6 +856,7 @@ protected:
|
||||
mozilla::ErrorResult& aError);
|
||||
|
||||
public:
|
||||
void Alert(mozilla::ErrorResult& aError);
|
||||
void Alert(const nsAString& aMessage, mozilla::ErrorResult& aError);
|
||||
bool Confirm(const nsAString& aMessage, mozilla::ErrorResult& aError);
|
||||
void Prompt(const nsAString& aMessage, const nsAString& aInitial,
|
||||
@ -1044,7 +1053,8 @@ protected:
|
||||
void DropOuterWindowDocs();
|
||||
void CleanUp();
|
||||
void ClearControllers();
|
||||
nsresult FinalClose();
|
||||
// Outer windows only.
|
||||
void FinalClose();
|
||||
|
||||
inline void MaybeClearInnerWindow(nsGlobalWindow* aExpectedInner)
|
||||
{
|
||||
@ -1343,6 +1353,7 @@ protected:
|
||||
bool aDefaultStylesOnly,
|
||||
nsIDOMCSSStyleDeclaration** aReturn);
|
||||
|
||||
// Outer windows only.
|
||||
void PreloadLocalStorage();
|
||||
|
||||
// Returns device pixels. Outer windows only.
|
||||
|
19
dom/base/nsISlowScriptDebug.idl
Normal file
19
dom/base/nsISlowScriptDebug.idl
Normal file
@ -0,0 +1,19 @@
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
interface nsIDOMWindow;
|
||||
|
||||
[scriptable, function, uuid(f7dbb80c-5d1e-4fd9-b55c-a9ffda4a75b1)]
|
||||
interface nsISlowScriptDebugCallback : nsISupports
|
||||
{
|
||||
void handleSlowScriptDebug(in nsIDOMWindow aWindow);
|
||||
};
|
||||
|
||||
[scriptable, uuid(f75d4164-3aa7-4395-ba44-a5f95b2e8427)]
|
||||
interface nsISlowScriptDebug : nsISupports
|
||||
{
|
||||
attribute nsISlowScriptDebugCallback activationHandler;
|
||||
};
|
@ -277,13 +277,11 @@ nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
}
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
|
||||
nsJSContext::NonIncrementalGC,
|
||||
nsJSContext::NonCompartmentGC,
|
||||
nsJSContext::ShrinkingGC);
|
||||
nsJSContext::CycleCollectNow();
|
||||
if (NeedsGCAfterCC()) {
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
|
||||
nsJSContext::NonIncrementalGC,
|
||||
nsJSContext::NonCompartmentGC,
|
||||
nsJSContext::ShrinkingGC);
|
||||
}
|
||||
} else if (!nsCRT::strcmp(aTopic, "quit-application")) {
|
||||
@ -388,10 +386,8 @@ AsyncErrorReporter::AsyncErrorReporter(JSRuntime* aRuntime,
|
||||
NS_LITERAL_CSTRING("content javascript");
|
||||
|
||||
mInnerWindowID = 0;
|
||||
if (aWindow && aWindow->IsOuterWindow()) {
|
||||
aWindow = aWindow->GetCurrentInnerWindow();
|
||||
}
|
||||
if (aWindow) {
|
||||
MOZ_ASSERT(aWindow->IsInnerWindow());
|
||||
mInnerWindowID = aWindow->WindowID();
|
||||
}
|
||||
}
|
||||
@ -429,8 +425,7 @@ AsyncErrorReporter::ReportError()
|
||||
class ScriptErrorEvent : public AsyncErrorReporter
|
||||
{
|
||||
public:
|
||||
ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
|
||||
JSRuntime* aRuntime,
|
||||
ScriptErrorEvent(JSRuntime* aRuntime,
|
||||
JSErrorReport* aErrorReport,
|
||||
const char* aFallbackMessage,
|
||||
nsIPrincipal* aScriptOriginPrincipal,
|
||||
@ -442,20 +437,21 @@ public:
|
||||
: AsyncErrorReporter(aRuntime, aErrorReport, aFallbackMessage,
|
||||
nsContentUtils::IsSystemPrincipal(aGlobalPrincipal),
|
||||
aWindow)
|
||||
, mScriptGlobal(aScriptGlobal)
|
||||
, mOriginPrincipal(aScriptOriginPrincipal)
|
||||
, mDispatchEvent(aDispatchEvent)
|
||||
, mError(aRuntime, aError)
|
||||
, mWindow(aWindow)
|
||||
{
|
||||
MOZ_ASSERT_IF(mWindow, mWindow->IsInnerWindow());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
// First, notify the DOM that we have a script error.
|
||||
if (mDispatchEvent) {
|
||||
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
|
||||
nsIDocShell* docShell = win ? win->GetDocShell() : nullptr;
|
||||
// First, notify the DOM that we have a script error, but only if
|
||||
// our window is still the current inner, if we're associated with a window.
|
||||
if (mDispatchEvent && (!mWindow || mWindow->IsCurrentInnerWindow())) {
|
||||
nsIDocShell* docShell = mWindow ? mWindow->GetDocShell() : nullptr;
|
||||
if (docShell &&
|
||||
!JSREPORT_IS_WARNING(mFlags) &&
|
||||
!sHandlingScriptError) {
|
||||
@ -471,7 +467,7 @@ public:
|
||||
init.mFilename = mFileName;
|
||||
init.mBubbles = true;
|
||||
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(mWindow));
|
||||
NS_ENSURE_STATE(sop);
|
||||
nsIPrincipal* p = sop->GetPrincipal();
|
||||
NS_ENSURE_STATE(p);
|
||||
@ -497,11 +493,11 @@ public:
|
||||
}
|
||||
|
||||
nsRefPtr<ErrorEvent> event =
|
||||
ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(win.get()),
|
||||
ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(mWindow.get()),
|
||||
NS_LITERAL_STRING("error"), init);
|
||||
event->SetTrusted(true);
|
||||
|
||||
EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
|
||||
EventDispatcher::DispatchDOMEvent(mWindow, nullptr, event, presContext,
|
||||
&status);
|
||||
}
|
||||
}
|
||||
@ -514,10 +510,10 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
|
||||
nsCOMPtr<nsIPrincipal> mOriginPrincipal;
|
||||
bool mDispatchEvent;
|
||||
JS::PersistentRootedValue mError;
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
|
||||
static bool sHandlingScriptError;
|
||||
};
|
||||
@ -575,13 +571,15 @@ NS_ScriptErrorReporter(JSContext *cx,
|
||||
if (globalObject) {
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
|
||||
if (win) {
|
||||
win = win->GetCurrentInnerWindow();
|
||||
}
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
|
||||
do_QueryInterface(globalObject);
|
||||
NS_ASSERTION(scriptPrincipal, "Global objects must implement "
|
||||
"nsIScriptObjectPrincipal");
|
||||
nsContentUtils::AddScriptRunner(
|
||||
new ScriptErrorEvent(globalObject,
|
||||
JS_GetRuntime(cx),
|
||||
new ScriptErrorEvent(JS_GetRuntime(cx),
|
||||
report,
|
||||
message,
|
||||
nsJSPrincipals::get(report->originPrincipals),
|
||||
@ -1708,7 +1706,6 @@ FullGCTimerFired(nsITimer* aTimer, void* aClosure)
|
||||
void
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
|
||||
IsIncremental aIncremental,
|
||||
IsCompartment aCompartment,
|
||||
IsShrinking aShrinking,
|
||||
int64_t aSliceMillis)
|
||||
{
|
||||
@ -2201,7 +2198,6 @@ InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
|
||||
nsJSContext::IncrementalGC,
|
||||
nsJSContext::CompartmentGC,
|
||||
nsJSContext::NonShrinkingGC,
|
||||
NS_INTERSLICE_GC_BUDGET);
|
||||
}
|
||||
@ -2213,8 +2209,7 @@ GCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
nsJSContext::KillGCTimer();
|
||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
|
||||
nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
|
||||
nsJSContext::IncrementalGC,
|
||||
nsJSContext::CompartmentGC);
|
||||
nsJSContext::IncrementalGC);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -68,11 +68,6 @@ public:
|
||||
static void LoadStart();
|
||||
static void LoadEnd();
|
||||
|
||||
enum IsCompartment {
|
||||
CompartmentGC,
|
||||
NonCompartmentGC
|
||||
};
|
||||
|
||||
enum IsShrinking {
|
||||
ShrinkingGC,
|
||||
NonShrinkingGC
|
||||
@ -88,7 +83,6 @@ public:
|
||||
|
||||
static void GarbageCollectNow(JS::gcreason::Reason reason,
|
||||
IsIncremental aIncremental = NonIncrementalGC,
|
||||
IsCompartment aCompartment = NonCompartmentGC,
|
||||
IsShrinking aShrinking = NonShrinkingGC,
|
||||
int64_t aSliceMillis = 0);
|
||||
static void ShrinkGCBuffersNow();
|
||||
|
@ -278,6 +278,7 @@ public:
|
||||
|
||||
// Set the window up with an about:blank document with the current subject
|
||||
// principal.
|
||||
// Outer windows only.
|
||||
virtual void SetInitialPrincipalToSubject() = 0;
|
||||
|
||||
virtual PopupControlState PushPopupControlState(PopupControlState aState,
|
||||
@ -364,7 +365,8 @@ public:
|
||||
return !IsInnerWindow();
|
||||
}
|
||||
|
||||
virtual bool WouldReuseInnerWindow(nsIDocument *aNewDocument) = 0;
|
||||
// Outer windows only.
|
||||
virtual bool WouldReuseInnerWindow(nsIDocument* aNewDocument) = 0;
|
||||
|
||||
/**
|
||||
* Get the docshell in this window.
|
||||
@ -424,8 +426,9 @@ public:
|
||||
virtual void EnterModalState() = 0;
|
||||
virtual void LeaveModalState() = 0;
|
||||
|
||||
// Outer windows only.
|
||||
virtual bool CanClose() = 0;
|
||||
virtual nsresult ForceClose() = 0;
|
||||
virtual void ForceClose() = 0;
|
||||
|
||||
bool IsModalContentWindow() const
|
||||
{
|
||||
|
@ -48,12 +48,9 @@ support-files =
|
||||
[test_named_frames.html]
|
||||
[test_navigator_resolve_identity.html]
|
||||
[test_navigator_language.html]
|
||||
[test_nondomexception.html]
|
||||
[test_openDialogChromeOnly.html]
|
||||
|
||||
[test_open_null_features.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
|
||||
|
||||
[test_postMessage_solidus.html]
|
||||
[test_screen_orientation.html]
|
||||
[test_settimeout_extra_arguments.html]
|
||||
|
@ -1,28 +0,0 @@
|
||||
<!-- intentionally omiting doctype because this test requires document.all -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for non-DOM module exceptions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
try {
|
||||
document.all();
|
||||
} catch (e) {
|
||||
is(typeof e, "object");
|
||||
is(e.filename, location);
|
||||
is(e.lineNumber, 18);
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
1038
dom/bluetooth2/BluetoothAdapter.cpp
Normal file
1038
dom/bluetooth2/BluetoothAdapter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
194
dom/bluetooth2/BluetoothAdapter.h
Normal file
194
dom/bluetooth2/BluetoothAdapter.h
Normal file
@ -0,0 +1,194 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothadapter_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothadapter_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothPropertyContainer.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class DOMRequest;
|
||||
struct MediaMetaData;
|
||||
struct MediaPlayStatus;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothDevice;
|
||||
class BluetoothSignal;
|
||||
class BluetoothNamedValue;
|
||||
class BluetoothValue;
|
||||
|
||||
class BluetoothAdapter : public DOMEventTargetHelper
|
||||
, public BluetoothSignalObserver
|
||||
, public BluetoothPropertyContainer
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(BluetoothAdapter,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
static already_AddRefed<BluetoothAdapter>
|
||||
Create(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
|
||||
|
||||
void Notify(const BluetoothSignal& aParam);
|
||||
|
||||
void Unroot();
|
||||
virtual void SetPropertyByValue(const BluetoothNamedValue& aValue) MOZ_OVERRIDE;
|
||||
|
||||
virtual void DisconnectFromOwner() MOZ_OVERRIDE;
|
||||
|
||||
void GetAddress(nsString& aAddress) const
|
||||
{
|
||||
aAddress = mAddress;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Class() const
|
||||
{
|
||||
return mClass;
|
||||
}
|
||||
|
||||
void
|
||||
GetName(nsString& aName) const
|
||||
{
|
||||
aName = mName;
|
||||
}
|
||||
|
||||
bool
|
||||
Discovering() const
|
||||
{
|
||||
return mDiscovering;
|
||||
}
|
||||
|
||||
bool
|
||||
Discoverable() const
|
||||
{
|
||||
return mDiscoverable;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
DiscoverableTimeout() const
|
||||
{
|
||||
return mDiscoverableTimeout;
|
||||
}
|
||||
|
||||
JS::Value GetDevices(JSContext* aContext, ErrorResult& aRv);
|
||||
JS::Value GetUuids(JSContext* aContext, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::DOMRequest>
|
||||
SetName(const nsAString& aName, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
SetDiscoverable(bool aDiscoverable, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetDiscoverableTimeout(uint32_t aTimeout, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> StartDiscovery(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> StopDiscovery(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
Pair(const nsAString& aDeviceAddress, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
GetPairedDevices(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetPinCode(const nsAString& aDeviceAddress, const nsAString& aPinCode,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetPasskey(const nsAString& aDeviceAddress, uint32_t aPasskey,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetPairingConfirmation(const nsAString& aDeviceAddress, bool aConfirmation,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SetAuthorization(const nsAString& aDeviceAddress, bool aAllow,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
Connect(BluetoothDevice& aDevice,
|
||||
const Optional<short unsigned int>& aServiceUuid, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
Disconnect(BluetoothDevice& aDevice,
|
||||
const Optional<short unsigned int>& aServiceUuid,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
GetConnectedDevices(uint16_t aServiceUuid, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
SendFile(const nsAString& aDeviceAddress, nsIDOMBlob* aBlob,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
StopSendingFile(const nsAString& aDeviceAddress, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
ConfirmReceivingFile(const nsAString& aDeviceAddress, bool aConfirmation,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest> ConnectSco(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> DisconnectSco(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> IsScoConnected(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest> AnswerWaitingCall(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> IgnoreWaitingCall(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> ToggleCalls(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
SendMediaMetaData(const MediaMetaData& aMediaMetaData, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
SendMediaPlayStatus(const MediaPlayStatus& aMediaPlayStatus, ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(devicefound);
|
||||
IMPL_EVENT_HANDLER(a2dpstatuschanged);
|
||||
IMPL_EVENT_HANDLER(hfpstatuschanged);
|
||||
IMPL_EVENT_HANDLER(pairedstatuschanged);
|
||||
IMPL_EVENT_HANDLER(requestmediaplaystatus);
|
||||
IMPL_EVENT_HANDLER(scostatuschanged);
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
BluetoothAdapter(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
|
||||
~BluetoothAdapter();
|
||||
|
||||
void Root();
|
||||
|
||||
already_AddRefed<mozilla::dom::DOMRequest>
|
||||
StartStopDiscovery(bool aStart, ErrorResult& aRv);
|
||||
already_AddRefed<mozilla::dom::DOMRequest>
|
||||
PairUnpair(bool aPair, const nsAString& aDeviceAddress, ErrorResult& aRv);
|
||||
|
||||
JS::Heap<JSObject*> mJsUuids;
|
||||
JS::Heap<JSObject*> mJsDeviceAddresses;
|
||||
nsString mAddress;
|
||||
nsString mName;
|
||||
bool mDiscoverable;
|
||||
bool mDiscovering;
|
||||
bool mPairable;
|
||||
bool mPowered;
|
||||
uint32_t mPairableTimeout;
|
||||
uint32_t mDiscoverableTimeout;
|
||||
uint32_t mClass;
|
||||
nsTArray<nsString> mDeviceAddresses;
|
||||
nsTArray<nsString> mUuids;
|
||||
bool mIsRooted;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif
|
163
dom/bluetooth2/BluetoothCommon.h
Normal file
163
dom/bluetooth2/BluetoothCommon.h
Normal file
@ -0,0 +1,163 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothcommon_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothcommon_h__
|
||||
|
||||
#include "mozilla/Observer.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
extern bool gBluetoothDebugFlag;
|
||||
|
||||
#define SWITCH_BT_DEBUG(V) (gBluetoothDebugFlag = V)
|
||||
|
||||
#undef BT_LOG
|
||||
#if defined(MOZ_WIDGET_GONK)
|
||||
#include <android/log.h>
|
||||
|
||||
/**
|
||||
* Prints 'D'EBUG build logs, which show in DEBUG build only when
|
||||
* developer setting 'Bluetooth output in adb' is enabled.
|
||||
*/
|
||||
#define BT_LOGD(msg, ...) \
|
||||
do { \
|
||||
if (gBluetoothDebugFlag) { \
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBluetooth", \
|
||||
"%s: " msg, __FUNCTION__, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* Prints 'R'ELEASE build logs, which show in both RELEASE and DEBUG builds.
|
||||
*/
|
||||
#define BT_LOGR(msg, ...) \
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBluetooth", \
|
||||
"%s: " msg, __FUNCTION__, ##__VA_ARGS__) \
|
||||
|
||||
/**
|
||||
* Prints DEBUG build warnings, which show in DEBUG build only.
|
||||
*/
|
||||
#define BT_WARNING(args...) \
|
||||
NS_WARNING(nsPrintfCString(args).get()) \
|
||||
|
||||
#else
|
||||
#define BT_LOGD(msg, ...) \
|
||||
do { \
|
||||
if (gBluetoothDebugFlag) { \
|
||||
printf("%s: " msg, __FUNCTION__, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define BT_LOGR(msg, ...) printf("%s: " msg, __FUNCTION__, ##__VA_ARGS__))
|
||||
#define BT_WARNING(msg, ...) printf("%s: " msg, __FUNCTION__, ##__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Wrap literal name and value into a BluetoothNamedValue
|
||||
* and append it to the array.
|
||||
*/
|
||||
#define BT_APPEND_NAMED_VALUE(array, name, value) \
|
||||
array.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING(name), value))
|
||||
|
||||
/**
|
||||
* Ensure success of system message broadcast with void return.
|
||||
*/
|
||||
#define BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters) \
|
||||
do { \
|
||||
if (!BroadcastSystemMessage(type, parameters)) { \
|
||||
BT_WARNING("Failed to broadcast [%s]", \
|
||||
NS_ConvertUTF16toUTF8(type).get()); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define BEGIN_BLUETOOTH_NAMESPACE \
|
||||
namespace mozilla { namespace dom { namespace bluetooth {
|
||||
#define END_BLUETOOTH_NAMESPACE \
|
||||
} /* namespace bluetooth */ } /* namespace dom */ } /* namespace mozilla */
|
||||
#define USING_BLUETOOTH_NAMESPACE \
|
||||
using namespace mozilla::dom::bluetooth;
|
||||
|
||||
#define KEY_LOCAL_AGENT "/B2G/bluetooth/agent"
|
||||
#define KEY_REMOTE_AGENT "/B2G/bluetooth/remote_device_agent"
|
||||
#define KEY_MANAGER "/B2G/bluetooth/manager"
|
||||
#define KEY_ADAPTER "/B2G/bluetooth/adapter"
|
||||
|
||||
/**
|
||||
* When the connection status of a Bluetooth profile is changed, we'll notify
|
||||
* observers which register the following topics.
|
||||
*/
|
||||
#define BLUETOOTH_A2DP_STATUS_CHANGED_ID "bluetooth-a2dp-status-changed"
|
||||
#define BLUETOOTH_HFP_STATUS_CHANGED_ID "bluetooth-hfp-status-changed"
|
||||
#define BLUETOOTH_HID_STATUS_CHANGED_ID "bluetooth-hid-status-changed"
|
||||
#define BLUETOOTH_SCO_STATUS_CHANGED_ID "bluetooth-sco-status-changed"
|
||||
|
||||
/**
|
||||
* When the connection status of a Bluetooth profile is changed, we'll
|
||||
* dispatch one of the following events.
|
||||
*/
|
||||
#define A2DP_STATUS_CHANGED_ID "a2dpstatuschanged"
|
||||
#define HFP_STATUS_CHANGED_ID "hfpstatuschanged"
|
||||
#define SCO_STATUS_CHANGED_ID "scostatuschanged"
|
||||
|
||||
/**
|
||||
* When the pair status of a Bluetooth device is changed, we'll dispatch an
|
||||
* event.
|
||||
*/
|
||||
#define PAIRED_STATUS_CHANGED_ID "pairedstatuschanged"
|
||||
|
||||
/**
|
||||
* When receiving a query about current play status from remote device, we'll
|
||||
* dispatch an event.
|
||||
*/
|
||||
#define REQUEST_MEDIA_PLAYSTATUS_ID "requestmediaplaystatus"
|
||||
|
||||
// Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx)
|
||||
#define BLUETOOTH_ADDRESS_LENGTH 17
|
||||
#define BLUETOOTH_ADDRESS_NONE "00:00:00:00:00:00"
|
||||
#define BLUETOOTH_ADDRESS_BYTES 6
|
||||
|
||||
// Bluetooth stack internal error, such as I/O error
|
||||
#define ERR_INTERNAL_ERROR "InternalError"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
enum BluetoothSocketType {
|
||||
RFCOMM = 1,
|
||||
SCO = 2,
|
||||
L2CAP = 3,
|
||||
EL2CAP = 4
|
||||
};
|
||||
|
||||
class BluetoothSignal;
|
||||
typedef mozilla::Observer<BluetoothSignal> BluetoothSignalObserver;
|
||||
|
||||
// Enums for object types, currently used for shared function lookups
|
||||
// (get/setproperty, etc...). Possibly discernable via dbus paths, but this
|
||||
// method is future-proofed for platform independence.
|
||||
enum BluetoothObjectType {
|
||||
TYPE_MANAGER = 0,
|
||||
TYPE_ADAPTER = 1,
|
||||
TYPE_DEVICE = 2,
|
||||
|
||||
TYPE_INVALID
|
||||
};
|
||||
|
||||
enum ControlPlayStatus {
|
||||
PLAYSTATUS_STOPPED = 0x00,
|
||||
PLAYSTATUS_PLAYING = 0x01,
|
||||
PLAYSTATUS_PAUSED = 0x02,
|
||||
PLAYSTATUS_FWD_SEEK = 0x03,
|
||||
PLAYSTATUS_REV_SEEK = 0x04,
|
||||
PLAYSTATUS_UNKNOWN,
|
||||
PLAYSTATUS_ERROR = 0xFF,
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_bluetooth_bluetoothcommon_h__
|
239
dom/bluetooth2/BluetoothDevice.cpp
Normal file
239
dom/bluetooth2/BluetoothDevice.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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 "base/basictypes.h"
|
||||
#include "BluetoothDevice.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "BluetoothUtils.h"
|
||||
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsTArrayHelpers.h"
|
||||
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/dom/BluetoothDevice2Binding.h"
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
DOMCI_DATA(BluetoothDevice, BluetoothDevice)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothDevice)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothDevice,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsUuids)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsServices)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothDevice,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothDevice,
|
||||
DOMEventTargetHelper)
|
||||
tmp->Unroot();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(BluetoothDevice, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(BluetoothDevice, DOMEventTargetHelper)
|
||||
|
||||
BluetoothDevice::BluetoothDevice(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aAdapterPath,
|
||||
const BluetoothValue& aValue)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, BluetoothPropertyContainer(BluetoothObjectType::TYPE_DEVICE)
|
||||
, mJsUuids(nullptr)
|
||||
, mJsServices(nullptr)
|
||||
, mAdapterPath(aAdapterPath)
|
||||
, mIsRooted(false)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(IsDOMBinding());
|
||||
|
||||
const InfallibleTArray<BluetoothNamedValue>& values =
|
||||
aValue.get_ArrayOfBluetoothNamedValue();
|
||||
for (uint32_t i = 0; i < values.Length(); ++i) {
|
||||
SetPropertyByValue(values[i]);
|
||||
}
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
bs->RegisterBluetoothSignalHandler(mAddress, this);
|
||||
}
|
||||
|
||||
BluetoothDevice::~BluetoothDevice()
|
||||
{
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
// bs can be null on shutdown, where destruction might happen.
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
bs->UnregisterBluetoothSignalHandler(mAddress, this);
|
||||
Unroot();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDevice::DisconnectFromOwner()
|
||||
{
|
||||
DOMEventTargetHelper::DisconnectFromOwner();
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
bs->UnregisterBluetoothSignalHandler(mAddress, this);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDevice::Root()
|
||||
{
|
||||
if (!mIsRooted) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
mIsRooted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDevice::Unroot()
|
||||
{
|
||||
if (mIsRooted) {
|
||||
mJsUuids = nullptr;
|
||||
mJsServices = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
mIsRooted = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
|
||||
{
|
||||
const nsString& name = aValue.name();
|
||||
const BluetoothValue& value = aValue.value();
|
||||
if (name.EqualsLiteral("Name")) {
|
||||
mName = value.get_nsString();
|
||||
} else if (name.EqualsLiteral("Path")) {
|
||||
MOZ_ASSERT(value.get_nsString().Length() > 0);
|
||||
mPath = value.get_nsString();
|
||||
} else if (name.EqualsLiteral("Address")) {
|
||||
mAddress = value.get_nsString();
|
||||
} else if (name.EqualsLiteral("Class")) {
|
||||
mClass = value.get_uint32_t();
|
||||
} else if (name.EqualsLiteral("Icon")) {
|
||||
mIcon = value.get_nsString();
|
||||
} else if (name.EqualsLiteral("Connected")) {
|
||||
mConnected = value.get_bool();
|
||||
} else if (name.EqualsLiteral("Paired")) {
|
||||
mPaired = value.get_bool();
|
||||
} else if (name.EqualsLiteral("UUIDs")) {
|
||||
mUuids = value.get_ArrayOfnsString();
|
||||
nsresult rv;
|
||||
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
NS_ENSURE_TRUE_VOID(sc);
|
||||
|
||||
AutoPushJSContext cx(sc->GetNativeContext());
|
||||
|
||||
JS::Rooted<JSObject*> uuids(cx);
|
||||
if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) {
|
||||
BT_WARNING("Cannot set JS UUIDs object!");
|
||||
return;
|
||||
}
|
||||
mJsUuids = uuids;
|
||||
Root();
|
||||
} else if (name.EqualsLiteral("Services")) {
|
||||
mServices = value.get_ArrayOfnsString();
|
||||
nsresult rv;
|
||||
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
NS_ENSURE_TRUE_VOID(sc);
|
||||
|
||||
AutoPushJSContext cx(sc->GetNativeContext());
|
||||
|
||||
JS::Rooted<JSObject*> services(cx);
|
||||
if (NS_FAILED(nsTArrayToJSArray(cx, mServices, services.address()))) {
|
||||
BT_WARNING("Cannot set JS Services object!");
|
||||
return;
|
||||
}
|
||||
mJsServices = services;
|
||||
Root();
|
||||
} else {
|
||||
nsCString warningMsg;
|
||||
warningMsg.AssignLiteral("Not handling device property: ");
|
||||
warningMsg.Append(NS_ConvertUTF16toUTF8(name));
|
||||
BT_WARNING(warningMsg.get());
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<BluetoothDevice>
|
||||
BluetoothDevice::Create(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aAdapterPath,
|
||||
const BluetoothValue& aValue)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
nsRefPtr<BluetoothDevice> device =
|
||||
new BluetoothDevice(aWindow, aAdapterPath, aValue);
|
||||
return device.forget();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDevice::Notify(const BluetoothSignal& aData)
|
||||
{
|
||||
BT_LOGD("[D] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
|
||||
|
||||
BluetoothValue v = aData.value();
|
||||
if (aData.name().EqualsLiteral("PropertyChanged")) {
|
||||
MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
|
||||
|
||||
const InfallibleTArray<BluetoothNamedValue>& arr =
|
||||
v.get_ArrayOfBluetoothNamedValue();
|
||||
|
||||
for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
|
||||
SetPropertyByValue(arr[i]);
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
nsCString warningMsg;
|
||||
warningMsg.AssignLiteral("Not handling device signal: ");
|
||||
warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
|
||||
BT_WARNING(warningMsg.get());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
JS::Value
|
||||
BluetoothDevice::GetUuids(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
if (!mJsUuids) {
|
||||
BT_WARNING("UUIDs not yet set!");
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
JS::ExposeObjectToActiveJS(mJsUuids);
|
||||
return JS::ObjectValue(*mJsUuids);
|
||||
}
|
||||
|
||||
JS::Value
|
||||
BluetoothDevice::GetServices(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
if (!mJsServices) {
|
||||
BT_WARNING("Services not yet set!");
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return JS::Value(JSVAL_NULL);
|
||||
}
|
||||
|
||||
JS::ExposeObjectToActiveJS(mJsServices);
|
||||
return JS::ObjectValue(*mJsServices);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
BluetoothDevice::WrapObject(JSContext* aContext)
|
||||
{
|
||||
return BluetoothDeviceBinding::Wrap(aContext, this);
|
||||
}
|
116
dom/bluetooth2/BluetoothDevice.h
Normal file
116
dom/bluetooth2/BluetoothDevice.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothdevice_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothdevice_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothPropertyContainer.h"
|
||||
#include "nsString.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothNamedValue;
|
||||
class BluetoothValue;
|
||||
class BluetoothSignal;
|
||||
class BluetoothSocket;
|
||||
|
||||
class BluetoothDevice : public DOMEventTargetHelper
|
||||
, public BluetoothSignalObserver
|
||||
, public BluetoothPropertyContainer
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(BluetoothDevice,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
static already_AddRefed<BluetoothDevice>
|
||||
Create(nsPIDOMWindow* aOwner, const nsAString& aAdapterPath,
|
||||
const BluetoothValue& aValue);
|
||||
|
||||
void Notify(const BluetoothSignal& aParam);
|
||||
|
||||
void GetAddress(nsString& aAddress) const
|
||||
{
|
||||
aAddress = mAddress;
|
||||
}
|
||||
|
||||
void GetName(nsString& aName) const
|
||||
{
|
||||
aName = mName;
|
||||
}
|
||||
|
||||
void GetIcon(nsString& aIcon) const
|
||||
{
|
||||
aIcon = mIcon;
|
||||
}
|
||||
|
||||
uint32_t Class() const
|
||||
{
|
||||
return mClass;
|
||||
}
|
||||
|
||||
bool Paired() const
|
||||
{
|
||||
return mPaired;
|
||||
}
|
||||
|
||||
bool Connected() const
|
||||
{
|
||||
return mConnected;
|
||||
}
|
||||
|
||||
JS::Value GetUuids(JSContext* aContext, ErrorResult& aRv);
|
||||
JS::Value GetServices(JSContext* aContext, ErrorResult& aRv);
|
||||
|
||||
nsISupports*
|
||||
ToISupports()
|
||||
{
|
||||
return static_cast<EventTarget*>(this);
|
||||
}
|
||||
|
||||
void SetPropertyByValue(const BluetoothNamedValue& aValue) MOZ_OVERRIDE;
|
||||
|
||||
void Unroot();
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
virtual void DisconnectFromOwner() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
BluetoothDevice(nsPIDOMWindow* aOwner, const nsAString& aAdapterPath,
|
||||
const BluetoothValue& aValue);
|
||||
~BluetoothDevice();
|
||||
void Root();
|
||||
|
||||
JS::Heap<JSObject*> mJsUuids;
|
||||
JS::Heap<JSObject*> mJsServices;
|
||||
|
||||
nsString mAdapterPath;
|
||||
nsString mAddress;
|
||||
nsString mName;
|
||||
nsString mIcon;
|
||||
uint32_t mClass;
|
||||
bool mConnected;
|
||||
bool mPaired;
|
||||
bool mIsRooted;
|
||||
nsTArray<nsString> mUuids;
|
||||
nsTArray<nsString> mServices;
|
||||
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif
|
29
dom/bluetooth2/BluetoothHfpManagerBase.h
Normal file
29
dom/bluetooth2/BluetoothHfpManagerBase.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothhfpmanagerbase_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothhfpmanagerbase_h__
|
||||
|
||||
#include "BluetoothProfileManagerBase.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothHfpManagerBase : public BluetoothProfileManagerBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns true if Sco is connected.
|
||||
*/
|
||||
virtual bool IsScoConnected() = 0;
|
||||
};
|
||||
|
||||
#define BT_DECL_HFP_MGR_BASE \
|
||||
BT_DECL_PROFILE_MGR_BASE \
|
||||
virtual bool IsScoConnected() MOZ_OVERRIDE;
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif //#ifndef mozilla_dom_bluetooth_bluetoothhfpmanagerbase_h__
|
270
dom/bluetooth2/BluetoothHidManager.cpp
Normal file
270
dom/bluetooth2/BluetoothHidManager.cpp
Normal file
@ -0,0 +1,270 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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 "base/basictypes.h"
|
||||
|
||||
#include "BluetoothHidManager.h"
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "BluetoothUtils.h"
|
||||
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "MainThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
namespace {
|
||||
StaticRefPtr<BluetoothHidManager> sBluetoothHidManager;
|
||||
bool sInShutdown = false;
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
BluetoothHidManager::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(sBluetoothHidManager);
|
||||
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
HandleShutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "BluetoothHidManager got unexpected topic!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
BluetoothHidManager::BluetoothHidManager()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::Reset()
|
||||
{
|
||||
mConnected = false;
|
||||
mController = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothHidManager::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, false);
|
||||
if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
|
||||
BT_WARNING("Failed to add shutdown observer!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BluetoothHidManager::~BluetoothHidManager()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_ENSURE_TRUE_VOID(obs);
|
||||
if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
|
||||
BT_WARNING("Failed to remove shutdown observer!");
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
BluetoothHidManager*
|
||||
BluetoothHidManager::Get()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// If we already exist, exit early
|
||||
if (sBluetoothHidManager) {
|
||||
return sBluetoothHidManager;
|
||||
}
|
||||
|
||||
// If we're in shutdown, don't create a new instance
|
||||
NS_ENSURE_FALSE(sInShutdown, nullptr);
|
||||
|
||||
// Create a new instance, register, and return
|
||||
BluetoothHidManager* manager = new BluetoothHidManager();
|
||||
NS_ENSURE_TRUE(manager->Init(), nullptr);
|
||||
|
||||
sBluetoothHidManager = manager;
|
||||
return sBluetoothHidManager;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::HandleShutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sInShutdown = true;
|
||||
Disconnect(nullptr);
|
||||
sBluetoothHidManager = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::Connect(const nsAString& aDeviceAddress,
|
||||
BluetoothProfileController* aController)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!aDeviceAddress.IsEmpty());
|
||||
MOZ_ASSERT(aController && !mController);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs || sInShutdown) {
|
||||
aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mConnected) {
|
||||
aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
|
||||
return;
|
||||
}
|
||||
|
||||
mDeviceAddress = aDeviceAddress;
|
||||
mController = aController;
|
||||
|
||||
if (NS_FAILED(bs->SendInputMessage(aDeviceAddress,
|
||||
NS_LITERAL_STRING("Connect")))) {
|
||||
aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::Disconnect(BluetoothProfileController* aController)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
if (aController) {
|
||||
aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mConnected) {
|
||||
if (aController) {
|
||||
aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mDeviceAddress.IsEmpty());
|
||||
MOZ_ASSERT(!mController);
|
||||
|
||||
mController = aController;
|
||||
|
||||
if (NS_FAILED(bs->SendInputMessage(mDeviceAddress,
|
||||
NS_LITERAL_STRING("Disconnect")))) {
|
||||
aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::OnConnect(const nsAString& aErrorStr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
/**
|
||||
* On the one hand, notify the controller that we've done for outbound
|
||||
* connections. On the other hand, we do nothing for inbound connections.
|
||||
*/
|
||||
NS_ENSURE_TRUE_VOID(mController);
|
||||
|
||||
nsRefPtr<BluetoothProfileController> controller = mController.forget();
|
||||
controller->NotifyCompletion(aErrorStr);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::OnDisconnect(const nsAString& aErrorStr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
/**
|
||||
* On the one hand, notify the controller that we've done for outbound
|
||||
* connections. On the other hand, we do nothing for inbound connections.
|
||||
*/
|
||||
NS_ENSURE_TRUE_VOID(mController);
|
||||
|
||||
nsRefPtr<BluetoothProfileController> controller = mController.forget();
|
||||
controller->NotifyCompletion(aErrorStr);
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothHidManager::IsConnected()
|
||||
{
|
||||
return mConnected;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::HandleInputPropertyChanged(const BluetoothSignal& aSignal)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue);
|
||||
|
||||
const InfallibleTArray<BluetoothNamedValue>& arr =
|
||||
aSignal.value().get_ArrayOfBluetoothNamedValue();
|
||||
MOZ_ASSERT(arr.Length() == 1);
|
||||
|
||||
const nsString& name = arr[0].name();
|
||||
const BluetoothValue& value = arr[0].value();
|
||||
|
||||
if (name.EqualsLiteral("Connected")) {
|
||||
MOZ_ASSERT(value.type() == BluetoothValue::Tbool);
|
||||
MOZ_ASSERT(mConnected != value.get_bool());
|
||||
|
||||
mConnected = value.get_bool();
|
||||
NotifyStatusChanged();
|
||||
if (mConnected) {
|
||||
OnConnect(EmptyString());
|
||||
} else {
|
||||
OnDisconnect(EmptyString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::NotifyStatusChanged()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
NS_NAMED_LITERAL_STRING(type, BLUETOOTH_HID_STATUS_CHANGED_ID);
|
||||
InfallibleTArray<BluetoothNamedValue> parameters;
|
||||
|
||||
BT_APPEND_NAMED_VALUE(parameters, "connected", mConnected);
|
||||
BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
|
||||
|
||||
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
|
||||
const nsAString& aServiceUuid,
|
||||
int aChannel)
|
||||
{
|
||||
// Do nothing here as bluez acquires service channel and connects for us
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
|
||||
{
|
||||
// Do nothing here as bluez acquires service channel and connects for us
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHidManager::GetAddress(nsAString& aDeviceAddress)
|
||||
{
|
||||
aDeviceAddress = mDeviceAddress;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(BluetoothHidManager, nsIObserver)
|
47
dom/bluetooth2/BluetoothHidManager.h
Normal file
47
dom/bluetooth2/BluetoothHidManager.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothhidmanager_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothhidmanager_h__
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothProfileController.h"
|
||||
#include "BluetoothProfileManagerBase.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothHidManager : public BluetoothProfileManagerBase
|
||||
{
|
||||
public:
|
||||
BT_DECL_PROFILE_MGR_BASE
|
||||
virtual void GetName(nsACString& aName)
|
||||
{
|
||||
aName.AssignLiteral("HID");
|
||||
}
|
||||
|
||||
static BluetoothHidManager* Get();
|
||||
virtual ~BluetoothHidManager();
|
||||
|
||||
// HID-specific functions
|
||||
void HandleInputPropertyChanged(const BluetoothSignal& aSignal);
|
||||
|
||||
private:
|
||||
BluetoothHidManager();
|
||||
bool Init();
|
||||
void Cleanup();
|
||||
void HandleShutdown();
|
||||
|
||||
void NotifyStatusChanged();
|
||||
|
||||
// data member
|
||||
bool mConnected;
|
||||
nsString mDeviceAddress;
|
||||
nsRefPtr<BluetoothProfileController> mController;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif //#ifndef mozilla_dom_bluetooth_bluetoothhidmanager_h__
|
252
dom/bluetooth2/BluetoothManager.cpp
Normal file
252
dom/bluetooth2/BluetoothManager.cpp
Normal file
@ -0,0 +1,252 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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 "base/basictypes.h"
|
||||
#include "BluetoothManager.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothAdapter.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
|
||||
#include "DOMRequest.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/dom/BluetoothManager2Binding.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
// QueryInterface implementation for BluetoothManager
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothManager)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(BluetoothManager, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(BluetoothManager, DOMEventTargetHelper)
|
||||
|
||||
class GetAdapterTask : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
GetAdapterTask(BluetoothManager* aManager,
|
||||
nsIDOMDOMRequest* aReq) :
|
||||
BluetoothReplyRunnable(aReq),
|
||||
mManagerPtr(aManager)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
aValue.setUndefined();
|
||||
|
||||
const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
|
||||
if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
|
||||
BT_WARNING("Not a BluetoothNamedValue array!");
|
||||
SetError(NS_LITERAL_STRING("BluetoothReplyTypeError"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mManagerPtr->GetOwner()) {
|
||||
BT_WARNING("Bluetooth manager was disconnected from owner window.");
|
||||
|
||||
// Stop to create adapter since owner window of Bluetooth manager was
|
||||
// gone. These is no need to create a DOMEvent target which has no owner
|
||||
// to reply to.
|
||||
return false;
|
||||
}
|
||||
|
||||
const InfallibleTArray<BluetoothNamedValue>& values =
|
||||
v.get_ArrayOfBluetoothNamedValue();
|
||||
nsRefPtr<BluetoothAdapter> adapter =
|
||||
BluetoothAdapter::Create(mManagerPtr->GetOwner(), values);
|
||||
|
||||
nsresult rv;
|
||||
nsIScriptContext* sc = mManagerPtr->GetContextForEventHandlers(&rv);
|
||||
if (!sc) {
|
||||
BT_WARNING("Cannot create script context!");
|
||||
SetError(NS_LITERAL_STRING("BluetoothScriptContextError"));
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoPushJSContext cx(sc->GetNativeContext());
|
||||
|
||||
JS::Rooted<JSObject*> scope(cx, sc->GetWindowProxy());
|
||||
JSAutoCompartment ac(cx, scope);
|
||||
rv = nsContentUtils::WrapNative(cx, adapter, aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
BT_WARNING("Cannot create native object!");
|
||||
SetError(NS_LITERAL_STRING("BluetoothNativeObjectError"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ReleaseMembers()
|
||||
{
|
||||
BluetoothReplyRunnable::ReleaseMembers();
|
||||
mManagerPtr = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothManager> mManagerPtr;
|
||||
};
|
||||
|
||||
BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, BluetoothPropertyContainer(BluetoothObjectType::TYPE_MANAGER)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(IsDOMBinding());
|
||||
|
||||
mPath.AssignLiteral("/");
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this);
|
||||
}
|
||||
|
||||
BluetoothManager::~BluetoothManager()
|
||||
{
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothManager::DisconnectFromOwner()
|
||||
{
|
||||
DOMEventTargetHelper::DisconnectFromOwner();
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothManager::SetPropertyByValue(const BluetoothNamedValue& aValue)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
const nsString& name = aValue.name();
|
||||
nsCString warningMsg;
|
||||
warningMsg.AssignLiteral("Not handling manager property: ");
|
||||
warningMsg.Append(NS_ConvertUTF16toUTF8(name));
|
||||
BT_WARNING(warningMsg.get());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothManager::GetEnabled(ErrorResult& aRv)
|
||||
{
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return bs->IsEnabled();
|
||||
}
|
||||
|
||||
already_AddRefed<dom::DOMRequest>
|
||||
BluetoothManager::GetDefaultAdapter(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
nsRefPtr<BluetoothReplyRunnable> results =
|
||||
new GetAdapterTask(this, request);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult rv = bs->GetDefaultAdapterPathInternal(results);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<BluetoothManager>
|
||||
BluetoothManager::Create(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
nsRefPtr<BluetoothManager> manager = new BluetoothManager(aWindow);
|
||||
return manager.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
BluetoothManager::CheckPermission(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
NS_ASSERTION(aWindow, "Null pointer!");
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
|
||||
NS_ENSURE_TRUE(permMgr, false);
|
||||
|
||||
uint32_t permission;
|
||||
nsresult rv =
|
||||
permMgr->TestPermissionFromWindow(aWindow, "bluetooth",
|
||||
&permission);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return permission == nsIPermissionManager::ALLOW_ACTION;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothManager::Notify(const BluetoothSignal& aData)
|
||||
{
|
||||
BT_LOGD("[M] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
|
||||
|
||||
if (aData.name().EqualsLiteral("AdapterAdded")) {
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("adapteradded"));
|
||||
} else if (aData.name().EqualsLiteral("Enabled")) {
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
|
||||
} else if (aData.name().EqualsLiteral("Disabled")) {
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
nsCString warningMsg;
|
||||
warningMsg.AssignLiteral("Not handling manager signal: ");
|
||||
warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
|
||||
BT_WARNING(warningMsg.get());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothManager::IsConnected(uint16_t aProfileId, ErrorResult& aRv)
|
||||
{
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return bs->IsConnected(aProfileId);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
BluetoothManager::WrapObject(JSContext* aCx)
|
||||
{
|
||||
return BluetoothManagerBinding::Wrap(aCx, this);
|
||||
}
|
67
dom/bluetooth2/BluetoothManager.h
Normal file
67
dom/bluetooth2/BluetoothManager.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothmanager_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothmanager_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/Observer.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothPropertyContainer.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class DOMRequest;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothNamedValue;
|
||||
|
||||
class BluetoothManager : public DOMEventTargetHelper
|
||||
, public BluetoothSignalObserver
|
||||
, public BluetoothPropertyContainer
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Never returns null
|
||||
static already_AddRefed<BluetoothManager>
|
||||
Create(nsPIDOMWindow* aWindow);
|
||||
static bool CheckPermission(nsPIDOMWindow* aWindow);
|
||||
void Notify(const BluetoothSignal& aData);
|
||||
virtual void SetPropertyByValue(const BluetoothNamedValue& aValue) MOZ_OVERRIDE;
|
||||
|
||||
bool GetEnabled(ErrorResult& aRv);
|
||||
bool IsConnected(uint16_t aProfileId, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest> GetDefaultAdapter(ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(enabled);
|
||||
IMPL_EVENT_HANDLER(disabled);
|
||||
IMPL_EVENT_HANDLER(adapteradded);
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
virtual void DisconnectFromOwner() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
BluetoothManager(nsPIDOMWindow* aWindow);
|
||||
~BluetoothManager();
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif
|
342
dom/bluetooth2/BluetoothProfileController.cpp
Normal file
342
dom/bluetooth2/BluetoothProfileController.cpp
Normal file
@ -0,0 +1,342 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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 "BluetoothProfileController.h"
|
||||
|
||||
#include "BluetoothA2dpManager.h"
|
||||
#include "BluetoothHfpManager.h"
|
||||
#include "BluetoothHidManager.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "BluetoothUtils.h"
|
||||
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
#define BT_LOGR_PROFILE(mgr, msg, ...) \
|
||||
do { \
|
||||
nsCString name; \
|
||||
mgr->GetName(name); \
|
||||
BT_LOGR("[%s] " msg, name.get(), ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define CONNECTION_TIMEOUT_MS 15000
|
||||
|
||||
class CheckProfileStatusCallback : public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
CheckProfileStatusCallback(BluetoothProfileController* aController)
|
||||
: mController(aController)
|
||||
{
|
||||
MOZ_ASSERT(aController);
|
||||
}
|
||||
|
||||
virtual ~CheckProfileStatusCallback()
|
||||
{
|
||||
mController = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothProfileController> mController;
|
||||
};
|
||||
|
||||
BluetoothProfileController::BluetoothProfileController(
|
||||
bool aConnect,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable,
|
||||
BluetoothProfileControllerCallback aCallback,
|
||||
uint16_t aServiceUuid,
|
||||
uint32_t aCod)
|
||||
: mConnect(aConnect)
|
||||
, mDeviceAddress(aDeviceAddress)
|
||||
, mRunnable(aRunnable)
|
||||
, mCallback(aCallback)
|
||||
, mCurrentProfileFinished(false)
|
||||
, mSuccess(false)
|
||||
, mProfilesIndex(-1)
|
||||
{
|
||||
MOZ_ASSERT(!aDeviceAddress.IsEmpty());
|
||||
MOZ_ASSERT(aRunnable);
|
||||
MOZ_ASSERT(aCallback);
|
||||
|
||||
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
MOZ_ASSERT(mTimer);
|
||||
|
||||
mProfiles.Clear();
|
||||
|
||||
/**
|
||||
* If the service uuid is not specified, either connect multiple profiles
|
||||
* based on Cod, or disconnect all connected profiles.
|
||||
*/
|
||||
if (!aServiceUuid) {
|
||||
mTarget.cod = aCod;
|
||||
SetupProfiles(false);
|
||||
} else {
|
||||
BluetoothServiceClass serviceClass =
|
||||
BluetoothUuidHelper::GetBluetoothServiceClass(aServiceUuid);
|
||||
mTarget.service = serviceClass;
|
||||
SetupProfiles(true);
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothProfileController::~BluetoothProfileController()
|
||||
{
|
||||
mProfiles.Clear();
|
||||
mRunnable = nullptr;
|
||||
mCallback = nullptr;
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothProfileController::AddProfileWithServiceClass(
|
||||
BluetoothServiceClass aClass)
|
||||
{
|
||||
BluetoothProfileManagerBase* profile;
|
||||
switch (aClass) {
|
||||
case BluetoothServiceClass::HANDSFREE:
|
||||
case BluetoothServiceClass::HEADSET:
|
||||
profile = BluetoothHfpManager::Get();
|
||||
break;
|
||||
case BluetoothServiceClass::A2DP:
|
||||
profile = BluetoothA2dpManager::Get();
|
||||
break;
|
||||
case BluetoothServiceClass::HID:
|
||||
profile = BluetoothHidManager::Get();
|
||||
break;
|
||||
default:
|
||||
DispatchBluetoothReply(mRunnable, BluetoothValue(),
|
||||
NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE));
|
||||
mCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
AddProfile(profile);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothProfileController::AddProfile(BluetoothProfileManagerBase* aProfile,
|
||||
bool aCheckConnected)
|
||||
{
|
||||
if (!aProfile) {
|
||||
DispatchBluetoothReply(mRunnable, BluetoothValue(),
|
||||
NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
||||
mCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (aCheckConnected && !aProfile->IsConnected()) {
|
||||
BT_WARNING("The profile is not connected.");
|
||||
return;
|
||||
}
|
||||
|
||||
mProfiles.AppendElement(aProfile);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
/**
|
||||
* When a service class is assigned, only its corresponding profile is put
|
||||
* into array.
|
||||
*/
|
||||
if (aAssignServiceClass) {
|
||||
AddProfileWithServiceClass(mTarget.service);
|
||||
return;
|
||||
}
|
||||
|
||||
// For a disconnect request, all connected profiles are put into array.
|
||||
if (!mConnect) {
|
||||
AddProfile(BluetoothHidManager::Get(), true);
|
||||
AddProfile(BluetoothA2dpManager::Get(), true);
|
||||
AddProfile(BluetoothHfpManager::Get(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a connect request, put multiple profiles into array and connect to
|
||||
* all of them sequencely.
|
||||
*/
|
||||
bool hasAudio = HAS_AUDIO(mTarget.cod);
|
||||
bool hasRendering = HAS_RENDERING(mTarget.cod);
|
||||
bool isPeripheral = IS_PERIPHERAL(mTarget.cod);
|
||||
bool isRemoteControl = IS_REMOTE_CONTROL(mTarget.cod);
|
||||
bool isKeyboard = IS_KEYBOARD(mTarget.cod);
|
||||
bool isPointingDevice = IS_POINTING_DEVICE(mTarget.cod);
|
||||
|
||||
NS_ENSURE_TRUE_VOID(hasAudio || hasRendering || isPeripheral);
|
||||
|
||||
// Audio bit should be set if remote device supports HFP/HSP.
|
||||
if (hasAudio) {
|
||||
AddProfile(BluetoothHfpManager::Get());
|
||||
}
|
||||
|
||||
// Rendering bit should be set if remote device supports A2DP.
|
||||
// A device which supports AVRCP should claim that it's a peripheral and it's
|
||||
// a remote control.
|
||||
if (hasRendering || (isPeripheral && isRemoteControl)) {
|
||||
AddProfile(BluetoothA2dpManager::Get());
|
||||
}
|
||||
|
||||
// A device which supports HID should claim that it's a peripheral and it's
|
||||
// either a keyboard, a pointing device, or both.
|
||||
if (isPeripheral && (isKeyboard || isPointingDevice)) {
|
||||
AddProfile(BluetoothHidManager::Get());
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(CheckProfileStatusCallback, nsITimerCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
CheckProfileStatusCallback::Notify(nsITimer* aTimer)
|
||||
{
|
||||
MOZ_ASSERT(mController);
|
||||
// Continue on the next profile since we haven't got the callback after
|
||||
// timeout.
|
||||
mController->GiveupAndContinue();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothProfileController::StartSession()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mDeviceAddress.IsEmpty());
|
||||
MOZ_ASSERT(mProfilesIndex == -1);
|
||||
MOZ_ASSERT(mTimer);
|
||||
|
||||
if (!IsBtServiceAvailable()) {
|
||||
EndSession();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mProfiles.Length() < 1) {
|
||||
BT_LOGR("No queued profile.");
|
||||
EndSession();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->InitWithCallback(new CheckProfileStatusCallback(this),
|
||||
CONNECTION_TIMEOUT_MS, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
BT_LOGR("%s", mConnect ? "connecting" : "disconnecting");
|
||||
|
||||
Next();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothProfileController::EndSession()
|
||||
{
|
||||
MOZ_ASSERT(mRunnable && mCallback);
|
||||
|
||||
BT_LOGR("mSuccess %d", mSuccess);
|
||||
|
||||
// Don't have to check profile status and retrigger session after connection
|
||||
// timeout, since session is end.
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
||||
// The action has completed, so the DOM request should be replied then invoke
|
||||
// the callback.
|
||||
if (mSuccess) {
|
||||
DispatchBluetoothReply(mRunnable, BluetoothValue(true), EmptyString());
|
||||
} else if (mConnect) {
|
||||
DispatchBluetoothReply(mRunnable, BluetoothValue(true),
|
||||
NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
|
||||
} else {
|
||||
DispatchBluetoothReply(mRunnable, BluetoothValue(true),
|
||||
NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
|
||||
}
|
||||
|
||||
mCallback();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothProfileController::Next()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mDeviceAddress.IsEmpty());
|
||||
MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
|
||||
MOZ_ASSERT(mTimer);
|
||||
|
||||
mCurrentProfileFinished = false;
|
||||
|
||||
if (!IsBtServiceAvailable()) {
|
||||
EndSession();
|
||||
return;
|
||||
}
|
||||
|
||||
if (++mProfilesIndex >= (int)mProfiles.Length()) {
|
||||
EndSession();
|
||||
return;
|
||||
}
|
||||
|
||||
BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "");
|
||||
|
||||
if (mConnect) {
|
||||
mProfiles[mProfilesIndex]->Connect(mDeviceAddress, this);
|
||||
} else {
|
||||
mProfiles[mProfilesIndex]->Disconnect(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothProfileController::IsBtServiceAvailable() const
|
||||
{
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
return (bs && bs->IsEnabled() && !bs->IsToggling());
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothProfileController::NotifyCompletion(const nsAString& aErrorStr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mTimer);
|
||||
MOZ_ASSERT(mProfiles.Length() > 0);
|
||||
|
||||
BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "<%s>",
|
||||
NS_ConvertUTF16toUTF8(aErrorStr).get());
|
||||
|
||||
mCurrentProfileFinished = true;
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
||||
mSuccess |= aErrorStr.IsEmpty();
|
||||
|
||||
Next();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothProfileController::GiveupAndContinue()
|
||||
{
|
||||
MOZ_ASSERT(!mCurrentProfileFinished);
|
||||
MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
|
||||
|
||||
BT_LOGR_PROFILE(mProfiles[mProfilesIndex], ERR_OPERATION_TIMEOUT);
|
||||
mProfiles[mProfilesIndex]->Reset();
|
||||
|
||||
if (IsBtServiceAvailable()) {
|
||||
Next();
|
||||
} else {
|
||||
EndSession();
|
||||
}
|
||||
}
|
||||
|
149
dom/bluetooth2/BluetoothProfileController.h
Normal file
149
dom/bluetooth2/BluetoothProfileController.h
Normal file
@ -0,0 +1,149 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothprofilecontroller_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothprofilecontroller_h__
|
||||
|
||||
#include "BluetoothUuid.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
/*
|
||||
* Class of Device(CoD): 32-bit unsigned integer
|
||||
*
|
||||
* 31 24 23 13 12 8 7 2 1 0
|
||||
* | | Major | Major | Minor | |
|
||||
* | | service | device | device | |
|
||||
* | | class | class | class | |
|
||||
* | |<- 11 ->|<- 5 ->|<- 6 ->| |
|
||||
*
|
||||
* https://www.bluetooth.org/en-us/specification/assigned-numbers/baseband
|
||||
*/
|
||||
|
||||
// Bit 23 ~ Bit 13: Major service class
|
||||
#define GET_MAJOR_SERVICE_CLASS(cod) ((cod & 0xffe000) >> 13)
|
||||
|
||||
// Bit 12 ~ Bit 8: Major device class
|
||||
#define GET_MAJOR_DEVICE_CLASS(cod) ((cod & 0x1f00) >> 8)
|
||||
|
||||
// Bit 7 ~ Bit 2: Minor device class
|
||||
#define GET_MINOR_DEVICE_CLASS(cod) ((cod & 0xfc) >> 2)
|
||||
|
||||
// Audio: Major service class = 0x100 (Bit 21 is set)
|
||||
#define HAS_AUDIO(cod) (cod & 0x200000)
|
||||
|
||||
// Rendering: Major service class = 0x20 (Bit 18 is set)
|
||||
#define HAS_RENDERING(cod) (cod & 0x40000)
|
||||
|
||||
// Peripheral: Major device class = 0x5
|
||||
#define IS_PERIPHERAL(cod) (GET_MAJOR_DEVICE_CLASS(cod) == 0x5)
|
||||
|
||||
// Remote Control: sub-field of minor device class, Bit 5 ~ Bit 2 = 0x3
|
||||
#define IS_REMOTE_CONTROL(cod) ((GET_MINOR_DEVICE_CLASS(cod) & 0xf) == 0x3)
|
||||
|
||||
// Keyboard: sub-field of minor device class (Bit 6)
|
||||
#define IS_KEYBOARD(cod) ((GET_MINOR_DEVICE_CLASS(cod) & 0x10) >> 4)
|
||||
|
||||
// Pointing device: sub-field of minor device class (Bit 7)
|
||||
#define IS_POINTING_DEVICE(cod) ((GET_MINOR_DEVICE_CLASS(cod) & 0x20) >> 5)
|
||||
|
||||
class BluetoothProfileManagerBase;
|
||||
class BluetoothReplyRunnable;
|
||||
typedef void (*BluetoothProfileControllerCallback)();
|
||||
|
||||
class BluetoothProfileController MOZ_FINAL
|
||||
{
|
||||
~BluetoothProfileController();
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(BluetoothProfileController)
|
||||
/**
|
||||
* @param aConnect: If it's a connect request, the value should be set
|
||||
* to true. For disconnect request, set it to false.
|
||||
* @param aDeviceAddress: The address of remote device.
|
||||
* @param aRunnable: Once the controller has done, the runnable will be
|
||||
* replied. When all connection/disconnection attemps
|
||||
* have failed, an error is fired. In other words,
|
||||
* reply a success if any attemp successes.
|
||||
* @param aCallback: The callback will be invoked after the runnable is
|
||||
* replied.
|
||||
* @param aServiceUuid: Connect/Disconnect to the specified profile. Please
|
||||
* see enum BluetoothServiceClass for valid value.
|
||||
* @param aCod: If aServiceUuid is not assigned, i.e. the value is
|
||||
* 0, the controller connect multiple profiles based on
|
||||
* aCod or disconnect all connected profiles.
|
||||
*/
|
||||
BluetoothProfileController(bool aConnect,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable,
|
||||
BluetoothProfileControllerCallback aCallback,
|
||||
uint16_t aServiceUuid,
|
||||
uint32_t aCod = 0);
|
||||
|
||||
/**
|
||||
* The controller starts connecting/disconnecting profiles one by one
|
||||
* according to the order in array mProfiles.
|
||||
*/
|
||||
void StartSession();
|
||||
|
||||
/**
|
||||
* The original DOM request would be fired in this function.
|
||||
*/
|
||||
void EndSession();
|
||||
|
||||
/**
|
||||
* It would be invoked after connect/disconnect operation is completed.
|
||||
* An error string would be returned when it fails.
|
||||
*/
|
||||
void NotifyCompletion(const nsAString& aErrorStr);
|
||||
|
||||
/**
|
||||
* It is invoked after a profile has reached timeout, reset mProfiles.
|
||||
*/
|
||||
void GiveupAndContinue();
|
||||
|
||||
private:
|
||||
// Setup data member mProfiles
|
||||
void SetupProfiles(bool aAssignServiceClass);
|
||||
|
||||
// Add profiles into array with/without checking connection status
|
||||
void AddProfile(BluetoothProfileManagerBase* aProfile,
|
||||
bool aCheckConnected = false);
|
||||
|
||||
// Add specified profile into array
|
||||
void AddProfileWithServiceClass(BluetoothServiceClass aClass);
|
||||
|
||||
// Connect/Disconnect next profile in the array
|
||||
void Next();
|
||||
|
||||
// Is Bluetooth service available for profile connection/disconnection ?
|
||||
bool IsBtServiceAvailable() const;
|
||||
|
||||
const bool mConnect;
|
||||
nsString mDeviceAddress;
|
||||
nsRefPtr<BluetoothReplyRunnable> mRunnable;
|
||||
BluetoothProfileControllerCallback mCallback;
|
||||
|
||||
bool mCurrentProfileFinished;
|
||||
bool mSuccess;
|
||||
int8_t mProfilesIndex;
|
||||
nsTArray<BluetoothProfileManagerBase*> mProfiles;
|
||||
|
||||
// Either CoD or BluetoothServiceClass is assigned.
|
||||
union {
|
||||
uint32_t cod;
|
||||
BluetoothServiceClass service;
|
||||
} mTarget;
|
||||
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif
|
98
dom/bluetooth2/BluetoothProfileManagerBase.h
Normal file
98
dom/bluetooth2/BluetoothProfileManagerBase.h
Normal file
@ -0,0 +1,98 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothprofilemanagerbase_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothprofilemanagerbase_h__
|
||||
|
||||
/**
|
||||
* Error Messages used in Bluetooth profiles
|
||||
*
|
||||
* These error messages would be sent to Gaia as an argument of onError event.
|
||||
*/
|
||||
#define ERR_ALREADY_CONNECTED "AlreadyConnectedError"
|
||||
#define ERR_ALREADY_DISCONNECTED "AlreadyDisconnectedError"
|
||||
#define ERR_CONNECTION_FAILED "ConnectionFailedError"
|
||||
#define ERR_DISCONNECTION_FAILED "DisconnectionFailedError"
|
||||
#define ERR_NO_AVAILABLE_RESOURCE "NoAvailableResourceError"
|
||||
#define ERR_REACHED_CONNECTION_LIMIT "ReachedConnectionLimitError"
|
||||
#define ERR_SERVICE_CHANNEL_NOT_FOUND "DeviceChannelRetrievalError"
|
||||
#define ERR_UNKNOWN_PROFILE "UnknownProfileError"
|
||||
#define ERR_OPERATION_TIMEOUT "OperationTimeout"
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include "nsIObserver.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
class BluetoothProfileController;
|
||||
|
||||
class BluetoothProfileManagerBase : public nsIObserver
|
||||
{
|
||||
public:
|
||||
virtual void OnGetServiceChannel(const nsAString& aDeviceAddress,
|
||||
const nsAString& aServiceUuid,
|
||||
int aChannel) = 0;
|
||||
virtual void OnUpdateSdpRecords(const nsAString& aDeviceAddress) = 0;
|
||||
|
||||
/**
|
||||
* Returns the address of the connected device.
|
||||
*/
|
||||
virtual void GetAddress(nsAString& aDeviceAddress) = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the profile is connected.
|
||||
*/
|
||||
virtual bool IsConnected() = 0;
|
||||
|
||||
/**
|
||||
* Connect to a specific remote device. When it has been done, the
|
||||
* callback "OnConnect" will be invoked.
|
||||
*/
|
||||
virtual void Connect(const nsAString& aDeviceAddress,
|
||||
BluetoothProfileController* aController) = 0;
|
||||
|
||||
/**
|
||||
* Close the socket and then invoke the callback "OnDisconnect".
|
||||
*/
|
||||
virtual void Disconnect(BluetoothProfileController* aController) = 0;
|
||||
|
||||
/**
|
||||
* If it establishes/releases a connection successfully, the error string
|
||||
* will be empty. Otherwise, the error string shows the failure reason.
|
||||
*/
|
||||
virtual void OnConnect(const nsAString& aErrorStr) = 0;
|
||||
virtual void OnDisconnect(const nsAString& aErrorStr) = 0;
|
||||
|
||||
/**
|
||||
* Clean up profile resources and set mController as null.
|
||||
*/
|
||||
virtual void Reset() = 0;
|
||||
|
||||
/**
|
||||
* Returns string of profile name.
|
||||
*/
|
||||
virtual void GetName(nsACString& aName) = 0;
|
||||
};
|
||||
|
||||
#define BT_DECL_PROFILE_MGR_BASE \
|
||||
public: \
|
||||
NS_DECL_ISUPPORTS \
|
||||
NS_DECL_NSIOBSERVER \
|
||||
virtual void OnGetServiceChannel(const nsAString& aDeviceAddress, \
|
||||
const nsAString& aServiceUuid, \
|
||||
int aChannel) MOZ_OVERRIDE; \
|
||||
virtual void OnUpdateSdpRecords(const nsAString& aDeviceAddress) MOZ_OVERRIDE; \
|
||||
virtual void GetAddress(nsAString& aDeviceAddress) MOZ_OVERRIDE; \
|
||||
virtual bool IsConnected() MOZ_OVERRIDE; \
|
||||
virtual void Connect(const nsAString& aDeviceAddress, \
|
||||
BluetoothProfileController* aController) MOZ_OVERRIDE; \
|
||||
virtual void Disconnect(BluetoothProfileController* aController) MOZ_OVERRIDE; \
|
||||
virtual void OnConnect(const nsAString& aErrorStr) MOZ_OVERRIDE; \
|
||||
virtual void OnDisconnect(const nsAString& AErrorStr) MOZ_OVERRIDE; \
|
||||
virtual void Reset() MOZ_OVERRIDE;
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif //#ifndef mozilla_dom_bluetooth_bluetoothprofilemanagerbase_h__
|
59
dom/bluetooth2/BluetoothPropertyContainer.cpp
Normal file
59
dom/bluetooth2/BluetoothPropertyContainer.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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 "base/basictypes.h"
|
||||
#include "BluetoothPropertyContainer.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "DOMRequest.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "js/Value.h"
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
already_AddRefed<mozilla::dom::DOMRequest>
|
||||
BluetoothPropertyContainer::FirePropertyAlreadySet(nsPIDOMWindow* aOwner,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIDOMRequestService> rs =
|
||||
do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
|
||||
if (!rs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<mozilla::dom::DOMRequest> request = new DOMRequest(aOwner);
|
||||
rs->FireSuccess(request, JS::UndefinedHandleValue);
|
||||
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::dom::DOMRequest>
|
||||
BluetoothPropertyContainer::SetProperty(nsPIDOMWindow* aOwner,
|
||||
const BluetoothNamedValue& aProperty,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<mozilla::dom::DOMRequest> request = new DOMRequest(aOwner);
|
||||
nsRefPtr<BluetoothReplyRunnable> task =
|
||||
new BluetoothVoidReplyRunnable(request);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
BT_WARNING("Bluetooth service not available!");
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult rv = bs->SetProperty(mObjectType, aProperty, task);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return request.forget();
|
||||
}
|
60
dom/bluetooth2/BluetoothPropertyContainer.h
Normal file
60
dom/bluetooth2/BluetoothPropertyContainer.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothpropertyobject_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothpropertyobject_h__
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
|
||||
class nsIDOMDOMRequest;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
namespace dom {
|
||||
class DOMRequest;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothNamedValue;
|
||||
|
||||
class BluetoothPropertyContainer
|
||||
{
|
||||
public:
|
||||
already_AddRefed<mozilla::dom::DOMRequest>
|
||||
FirePropertyAlreadySet(nsPIDOMWindow* aOwner, ErrorResult& aRv);
|
||||
already_AddRefed<mozilla::dom::DOMRequest>
|
||||
SetProperty(nsPIDOMWindow* aOwner, const BluetoothNamedValue& aProperty,
|
||||
ErrorResult& aRv);
|
||||
virtual void SetPropertyByValue(const BluetoothNamedValue& aValue) = 0;
|
||||
nsString GetPath()
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
// Compatibility with nsRefPtr to make sure we don't hold a weakptr to
|
||||
// ourselves
|
||||
virtual nsrefcnt AddRef() = 0;
|
||||
virtual nsrefcnt Release() = 0;
|
||||
|
||||
protected:
|
||||
BluetoothPropertyContainer(BluetoothObjectType aType) :
|
||||
mObjectType(aType)
|
||||
{}
|
||||
|
||||
~BluetoothPropertyContainer()
|
||||
{}
|
||||
|
||||
nsString mPath;
|
||||
BluetoothObjectType mObjectType;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif
|
96
dom/bluetooth2/BluetoothReplyRunnable.cpp
Normal file
96
dom/bluetooth2/BluetoothReplyRunnable.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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 "base/basictypes.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
#include "DOMRequest.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
BluetoothReplyRunnable::BluetoothReplyRunnable(nsIDOMDOMRequest* aReq)
|
||||
: mDOMRequest(aReq)
|
||||
{}
|
||||
|
||||
void
|
||||
BluetoothReplyRunnable::SetReply(BluetoothReply* aReply)
|
||||
{
|
||||
mReply = aReply;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothReplyRunnable::ReleaseMembers()
|
||||
{
|
||||
mDOMRequest = nullptr;
|
||||
}
|
||||
|
||||
BluetoothReplyRunnable::~BluetoothReplyRunnable()
|
||||
{}
|
||||
|
||||
nsresult
|
||||
BluetoothReplyRunnable::FireReply(JS::Handle<JS::Value> aVal)
|
||||
{
|
||||
nsCOMPtr<nsIDOMRequestService> rs =
|
||||
do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
|
||||
|
||||
return mReply->type() == BluetoothReply::TBluetoothReplySuccess ?
|
||||
rs->FireSuccessAsync(mDOMRequest, aVal) :
|
||||
rs->FireErrorAsync(mDOMRequest, mReply->get_BluetoothReplyError().error());
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothReplyRunnable::FireErrorString()
|
||||
{
|
||||
nsCOMPtr<nsIDOMRequestService> rs =
|
||||
do_GetService("@mozilla.org/dom/dom-request-service;1");
|
||||
NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
|
||||
|
||||
return rs->FireErrorAsync(mDOMRequest, mErrorString);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BluetoothReplyRunnable::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mDOMRequest);
|
||||
MOZ_ASSERT(mReply);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JS::Value> v(cx, JSVAL_VOID);
|
||||
|
||||
if (mReply->type() != BluetoothReply::TBluetoothReplySuccess) {
|
||||
rv = FireReply(v);
|
||||
} else {
|
||||
if (!ParseSuccessfulReply(&v)) {
|
||||
rv = FireErrorString();
|
||||
} else {
|
||||
rv = FireReply(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
BT_WARNING("Could not fire DOMRequest!");
|
||||
}
|
||||
|
||||
ReleaseMembers();
|
||||
MOZ_ASSERT(!mDOMRequest,
|
||||
"mDOMRequest still alive! Deriving class should call "
|
||||
"BluetoothReplyRunnable::ReleaseMembers()!");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
BluetoothVoidReplyRunnable::BluetoothVoidReplyRunnable(nsIDOMDOMRequest* aReq)
|
||||
: BluetoothReplyRunnable(aReq)
|
||||
{}
|
||||
|
||||
BluetoothVoidReplyRunnable::~BluetoothVoidReplyRunnable()
|
||||
{}
|
||||
|
71
dom/bluetooth2/BluetoothReplyRunnable.h
Normal file
71
dom/bluetooth2/BluetoothReplyRunnable.h
Normal file
@ -0,0 +1,71 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothreplyrunnable_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothreplyrunnable_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "js/Value.h"
|
||||
|
||||
class nsIDOMDOMRequest;
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothReply;
|
||||
|
||||
class BluetoothReplyRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
BluetoothReplyRunnable(nsIDOMDOMRequest* aReq);
|
||||
|
||||
void SetReply(BluetoothReply* aReply);
|
||||
|
||||
void SetError(const nsAString& aError)
|
||||
{
|
||||
mErrorString = aError;
|
||||
}
|
||||
|
||||
virtual void ReleaseMembers();
|
||||
|
||||
protected:
|
||||
virtual ~BluetoothReplyRunnable();
|
||||
|
||||
virtual bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) = 0;
|
||||
|
||||
// This is an autoptr so we don't have to bring the ipdl include into the
|
||||
// header. We assume we'll only be running this once and it should die on
|
||||
// scope out of Run() anyways.
|
||||
nsAutoPtr<BluetoothReply> mReply;
|
||||
|
||||
private:
|
||||
nsresult FireReply(JS::Handle<JS::Value> aVal);
|
||||
nsresult FireErrorString();
|
||||
|
||||
nsCOMPtr<nsIDOMDOMRequest> mDOMRequest;
|
||||
nsString mErrorString;
|
||||
};
|
||||
|
||||
class BluetoothVoidReplyRunnable : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
BluetoothVoidReplyRunnable(nsIDOMDOMRequest* aReq);
|
||||
~BluetoothVoidReplyRunnable();
|
||||
|
||||
protected:
|
||||
virtual bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) MOZ_OVERRIDE
|
||||
{
|
||||
aValue.setUndefined();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif
|
431
dom/bluetooth2/BluetoothRilListener.cpp
Normal file
431
dom/bluetooth2/BluetoothRilListener.cpp
Normal file
@ -0,0 +1,431 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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 "BluetoothRilListener.h"
|
||||
|
||||
#include "BluetoothHfpManager.h"
|
||||
#include "nsIMobileConnectionInfo.h"
|
||||
#include "nsIRadioInterfaceLayer.h"
|
||||
#include "nsRadioInterfaceLayer.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsString.h"
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
/**
|
||||
* IccListener
|
||||
*/
|
||||
NS_IMPL_ISUPPORTS(IccListener, nsIIccListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
IccListener::NotifyIccInfoChanged()
|
||||
{
|
||||
// mOwner would be set to nullptr only in the dtor of BluetoothRilListener
|
||||
NS_ENSURE_TRUE(mOwner, NS_ERROR_FAILURE);
|
||||
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(hfp, NS_ERROR_FAILURE);
|
||||
|
||||
hfp->HandleIccInfoChanged(mOwner->mClientId);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IccListener::NotifyStkCommand(const nsAString & aMessage)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IccListener::NotifyStkSessionEnd()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IccListener::NotifyCardStateChanged()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
IccListener::Listen(bool aStart)
|
||||
{
|
||||
NS_ENSURE_TRUE(mOwner, false);
|
||||
|
||||
nsCOMPtr<nsIIccProvider> provider =
|
||||
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(provider, false);
|
||||
|
||||
nsresult rv;
|
||||
if (aStart) {
|
||||
rv = provider->RegisterIccMsg(mOwner->mClientId, this);
|
||||
} else {
|
||||
rv = provider->UnregisterIccMsg(mOwner->mClientId, this);
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
void
|
||||
IccListener::SetOwner(BluetoothRilListener *aOwner)
|
||||
{
|
||||
mOwner = aOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* MobileConnectionListener
|
||||
*/
|
||||
NS_IMPL_ISUPPORTS(MobileConnectionListener, nsIMobileConnectionListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyVoiceChanged()
|
||||
{
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(hfp, NS_OK);
|
||||
|
||||
hfp->HandleVoiceConnectionChanged(mClientId);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyDataChanged()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyUssdReceived(const nsAString & message,
|
||||
bool sessionEnded)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyDataError(const nsAString & message)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyCFStateChange(bool success,
|
||||
uint16_t action,
|
||||
uint16_t reason,
|
||||
const nsAString& number,
|
||||
uint16_t timeSeconds,
|
||||
uint16_t serviceClass)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyEmergencyCbModeChanged(bool active,
|
||||
uint32_t timeoutMs)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyOtaStatusChanged(const nsAString & status)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyIccChanged()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyRadioStateChanged()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnectionListener::NotifyClirModeChanged(uint32_t aMode)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
MobileConnectionListener::Listen(bool aStart)
|
||||
{
|
||||
nsCOMPtr<nsIMobileConnectionProvider> provider =
|
||||
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(provider, false);
|
||||
|
||||
nsresult rv;
|
||||
if (aStart) {
|
||||
rv = provider->RegisterMobileConnectionMsg(mClientId, this);
|
||||
} else {
|
||||
rv = provider->UnregisterMobileConnectionMsg(mClientId, this);
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* TelephonyListener Implementation
|
||||
*/
|
||||
NS_IMPL_ISUPPORTS(TelephonyListener, nsITelephonyListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::CallStateChanged(uint32_t aServiceId,
|
||||
uint32_t aCallIndex,
|
||||
uint16_t aCallState,
|
||||
const nsAString& aNumber,
|
||||
bool aIsActive,
|
||||
bool aIsOutgoing,
|
||||
bool aIsEmergency,
|
||||
bool aIsConference,
|
||||
bool aIsSwitchable,
|
||||
bool aIsMergeable)
|
||||
{
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(hfp, NS_ERROR_FAILURE);
|
||||
|
||||
hfp->HandleCallStateChanged(aCallIndex, aCallState, EmptyString(), aNumber,
|
||||
aIsOutgoing, aIsConference, true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::EnumerateCallState(uint32_t aServiceId,
|
||||
uint32_t aCallIndex,
|
||||
uint16_t aCallState,
|
||||
const nsAString_internal& aNumber,
|
||||
bool aIsActive,
|
||||
bool aIsOutgoing,
|
||||
bool aIsEmergency,
|
||||
bool aIsConference,
|
||||
bool aIsSwitchable,
|
||||
bool aIsMergeable)
|
||||
{
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(hfp, NS_ERROR_FAILURE);
|
||||
|
||||
hfp->HandleCallStateChanged(aCallIndex, aCallState, EmptyString(), aNumber,
|
||||
aIsOutgoing, aIsConference, false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::NotifyError(uint32_t aServiceId,
|
||||
int32_t aCallIndex,
|
||||
const nsAString& aError)
|
||||
{
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(hfp, NS_ERROR_FAILURE);
|
||||
|
||||
if (aCallIndex > 0) {
|
||||
// In order to not miss any related call state transition.
|
||||
// It's possible that 3G network signal lost for unknown reason.
|
||||
// If a call is released abnormally, NotifyError() will be called,
|
||||
// instead of CallStateChanged(). We need to reset the call array state
|
||||
// via setting CALL_STATE_DISCONNECTED
|
||||
hfp->HandleCallStateChanged(aCallIndex,
|
||||
nsITelephonyProvider::CALL_STATE_DISCONNECTED,
|
||||
aError, EmptyString(), false, false, true);
|
||||
BT_WARNING("Reset the call state due to call transition ends abnormally");
|
||||
}
|
||||
|
||||
BT_WARNING(NS_ConvertUTF16toUTF8(aError).get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::ConferenceCallStateChanged(uint16_t aCallState)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::EnumerateCallStateComplete()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::SupplementaryServiceNotification(uint32_t aServiceId,
|
||||
int32_t aCallIndex,
|
||||
uint16_t aNotification)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::NotifyConferenceError(const nsAString& aName,
|
||||
const nsAString& aMessage)
|
||||
{
|
||||
BT_WARNING(NS_ConvertUTF16toUTF8(aName).get());
|
||||
BT_WARNING(NS_ConvertUTF16toUTF8(aMessage).get());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::NotifyCdmaCallWaiting(uint32_t aServiceId,
|
||||
const nsAString& aNumber)
|
||||
{
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(hfp, NS_ERROR_FAILURE);
|
||||
|
||||
hfp->UpdateSecondNumber(aNumber);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
TelephonyListener::Listen(bool aStart)
|
||||
{
|
||||
nsCOMPtr<nsITelephonyProvider> provider =
|
||||
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(provider, false);
|
||||
|
||||
nsresult rv;
|
||||
if (aStart) {
|
||||
rv = provider->RegisterListener(this);
|
||||
} else {
|
||||
rv = provider->UnregisterListener(this);
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* BluetoothRilListener
|
||||
*/
|
||||
BluetoothRilListener::BluetoothRilListener()
|
||||
{
|
||||
// Query number of total clients (sim slots)
|
||||
uint32_t numOfClients;
|
||||
nsCOMPtr<nsIRadioInterfaceLayer> radioInterfaceLayer =
|
||||
do_GetService(NS_RADIOINTERFACELAYER_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(radioInterfaceLayer);
|
||||
|
||||
radioInterfaceLayer->GetNumRadioInterfaces(&numOfClients);
|
||||
|
||||
// Init MobileConnectionListener array and IccInfoListener
|
||||
for (uint32_t i = 0; i < numOfClients; i++) {
|
||||
mMobileConnListeners.AppendElement(new MobileConnectionListener(i));
|
||||
}
|
||||
|
||||
mTelephonyListener = new TelephonyListener();
|
||||
mIccListener = new IccListener();
|
||||
mIccListener->SetOwner(this);
|
||||
|
||||
// Probe for available client
|
||||
SelectClient();
|
||||
}
|
||||
|
||||
BluetoothRilListener::~BluetoothRilListener()
|
||||
{
|
||||
mIccListener->SetOwner(nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRilListener::Listen(bool aStart)
|
||||
{
|
||||
NS_ENSURE_TRUE(ListenMobileConnAndIccInfo(aStart), false);
|
||||
NS_ENSURE_TRUE(mTelephonyListener->Listen(aStart), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothRilListener::SelectClient()
|
||||
{
|
||||
// Reset mClientId
|
||||
mClientId = mMobileConnListeners.Length();
|
||||
|
||||
nsCOMPtr<nsIMobileConnectionProvider> connection =
|
||||
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(connection);
|
||||
|
||||
for (uint32_t i = 0; i < mMobileConnListeners.Length(); i++) {
|
||||
nsCOMPtr<nsIMobileConnectionInfo> voiceInfo;
|
||||
connection->GetVoiceConnectionInfo(i, getter_AddRefs(voiceInfo));
|
||||
if (!voiceInfo) {
|
||||
BT_WARNING("%s: Failed to get voice connection info", __FUNCTION__);
|
||||
continue;
|
||||
}
|
||||
|
||||
nsString regState;
|
||||
voiceInfo->GetState(regState);
|
||||
if (regState.EqualsLiteral("registered")) {
|
||||
// Found available client
|
||||
mClientId = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothRilListener::ServiceChanged(uint32_t aClientId, bool aRegistered)
|
||||
{
|
||||
// Stop listening
|
||||
ListenMobileConnAndIccInfo(false);
|
||||
|
||||
/**
|
||||
* aRegistered:
|
||||
* - TRUE: service becomes registered. We were listening to all clients
|
||||
* and one of them becomes available. Select it to listen.
|
||||
* - FALSE: service becomes un-registered. The client we were listening
|
||||
* becomes unavailable. Select another registered one to listen.
|
||||
*/
|
||||
if (aRegistered) {
|
||||
mClientId = aClientId;
|
||||
} else {
|
||||
SelectClient();
|
||||
}
|
||||
|
||||
// Restart listening
|
||||
ListenMobileConnAndIccInfo(true);
|
||||
|
||||
BT_LOGR("%d client %d. new mClientId %d", aRegistered, aClientId,
|
||||
(mClientId < mMobileConnListeners.Length()) ? mClientId : -1);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothRilListener::EnumerateCalls()
|
||||
{
|
||||
nsCOMPtr<nsITelephonyProvider> provider =
|
||||
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(provider);
|
||||
|
||||
nsCOMPtr<nsITelephonyListener> listener(
|
||||
do_QueryObject(mTelephonyListener));
|
||||
|
||||
provider->EnumerateCalls(listener);
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRilListener::ListenMobileConnAndIccInfo(bool aStart)
|
||||
{
|
||||
/**
|
||||
* mClientId < number of total clients:
|
||||
* The client with mClientId is available. Start/Stop listening
|
||||
* mobile connection and icc info of this client only.
|
||||
*
|
||||
* mClientId >= number of total clients:
|
||||
* All clients are unavailable. Start/Stop listening mobile
|
||||
* connections of all clients.
|
||||
*/
|
||||
if (mClientId < mMobileConnListeners.Length()) {
|
||||
NS_ENSURE_TRUE(mMobileConnListeners[mClientId]->Listen(aStart), false);
|
||||
NS_ENSURE_TRUE(mIccListener->Listen(aStart), false);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < mMobileConnListeners.Length(); i++) {
|
||||
NS_ENSURE_TRUE(mMobileConnListeners[i]->Listen(aStart), false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
130
dom/bluetooth2/BluetoothRilListener.h
Normal file
130
dom/bluetooth2/BluetoothRilListener.h
Normal file
@ -0,0 +1,130 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothrillistener_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothrillistener_h__
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
#include "nsIIccProvider.h"
|
||||
#include "nsIMobileConnectionProvider.h"
|
||||
#include "nsITelephonyProvider.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothRilListener;
|
||||
|
||||
class IccListener : public nsIIccListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIICCLISTENER
|
||||
|
||||
IccListener() { }
|
||||
virtual ~IccListener() { }
|
||||
|
||||
bool Listen(bool aStart);
|
||||
void SetOwner(BluetoothRilListener *aOwner);
|
||||
|
||||
private:
|
||||
BluetoothRilListener* mOwner;
|
||||
};
|
||||
|
||||
class MobileConnectionListener : public nsIMobileConnectionListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMOBILECONNECTIONLISTENER
|
||||
|
||||
MobileConnectionListener(uint32_t aClientId)
|
||||
: mClientId(aClientId) { }
|
||||
virtual ~MobileConnectionListener() { }
|
||||
|
||||
bool Listen(bool aStart);
|
||||
|
||||
private:
|
||||
uint32_t mClientId;
|
||||
};
|
||||
|
||||
class TelephonyListener : public nsITelephonyListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITELEPHONYLISTENER
|
||||
|
||||
TelephonyListener() { }
|
||||
virtual ~TelephonyListener() { }
|
||||
|
||||
bool Listen(bool aStart);
|
||||
};
|
||||
|
||||
class BluetoothRilListener
|
||||
{
|
||||
public:
|
||||
BluetoothRilListener();
|
||||
~BluetoothRilListener();
|
||||
|
||||
/**
|
||||
* Start/Stop listening.
|
||||
*
|
||||
* @param aStart [in] whether to start/stop listening
|
||||
*/
|
||||
bool Listen(bool aStart);
|
||||
|
||||
/**
|
||||
* Be informed that certain client's service has changed.
|
||||
*
|
||||
* @param aClientId [in] the client id with service change
|
||||
* @param aRegistered [in] whether changed service is registered
|
||||
*/
|
||||
void ServiceChanged(uint32_t aClientId, bool aRegistered);
|
||||
|
||||
/**
|
||||
* Enumerate current calls.
|
||||
*/
|
||||
void EnumerateCalls();
|
||||
|
||||
/**
|
||||
* The id of client that mobile connection and icc info listeners
|
||||
* are listening to.
|
||||
*
|
||||
* mClientId equals to number of total clients (array length of
|
||||
* mobile connection listeners) if there is no available client to listen.
|
||||
*/
|
||||
uint32_t mClientId;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Start/Stop listening of mobile connection and icc info.
|
||||
*
|
||||
* @param aStart [in] whether to start/stop listening
|
||||
*/
|
||||
bool ListenMobileConnAndIccInfo(bool aStart);
|
||||
|
||||
/**
|
||||
* Select available client to listen and assign mClientId.
|
||||
*
|
||||
* mClientId is assigned to number of total clients (array length of
|
||||
* mobile connection listeners) if there is no available client to listen.
|
||||
*/
|
||||
void SelectClient();
|
||||
|
||||
/**
|
||||
* Array of mobile connection listeners.
|
||||
*
|
||||
* The length equals to number of total clients.
|
||||
*/
|
||||
nsTArray<nsRefPtr<MobileConnectionListener> > mMobileConnListeners;
|
||||
|
||||
nsRefPtr<IccListener> mIccListener;
|
||||
nsRefPtr<TelephonyListener> mTelephonyListener;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif
|
846
dom/bluetooth2/BluetoothService.cpp
Normal file
846
dom/bluetooth2/BluetoothService.cpp
Normal file
@ -0,0 +1,846 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=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 "base/basictypes.h"
|
||||
|
||||
#include "BluetoothService.h"
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothA2dpManager.h"
|
||||
#include "BluetoothHfpManager.h"
|
||||
#include "BluetoothHidManager.h"
|
||||
#include "BluetoothManager.h"
|
||||
#include "BluetoothOppManager.h"
|
||||
#include "BluetoothParent.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
#include "BluetoothServiceChildProcess.h"
|
||||
#include "BluetoothUtils.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/ipc/UnixSocket.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsISettingsService.h"
|
||||
#include "nsISystemMessagesInternal.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsXPCOM.h"
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK)
|
||||
#include "cutils/properties.h"
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_B2G_BT)
|
||||
#if defined(MOZ_B2G_BT_BLUEZ)
|
||||
/**
|
||||
* B2G blueZ:
|
||||
* MOZ_B2G_BT and MOZ_B2G_BT_BLUEZ are both defined.
|
||||
*/
|
||||
#include "BluetoothDBusService.h"
|
||||
#elif defined(MOZ_B2G_BT_BLUEDROID)
|
||||
/**
|
||||
* B2G bluedroid:
|
||||
* MOZ_B2G_BT and MOZ_B2G_BT_BLUEDROID are both defined;
|
||||
* MOZ_B2G_BLUEZ is not defined.
|
||||
*/
|
||||
#include "BluetoothServiceBluedroid.h"
|
||||
#endif
|
||||
#elif defined(MOZ_BLUETOOTH_DBUS)
|
||||
/**
|
||||
* Desktop bluetooth:
|
||||
* MOZ_B2G_BT is not defined; MOZ_BLUETOOTH_DBUS is defined.
|
||||
*/
|
||||
#include "BluetoothDBusService.h"
|
||||
#else
|
||||
#error No backend
|
||||
#endif
|
||||
|
||||
#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
|
||||
#define BLUETOOTH_ENABLED_SETTING "bluetooth.enabled"
|
||||
#define BLUETOOTH_DEBUGGING_SETTING "bluetooth.debugging.enabled"
|
||||
|
||||
#define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled"
|
||||
|
||||
#define DEFAULT_SHUTDOWN_TIMER_MS 5000
|
||||
|
||||
bool gBluetoothDebugFlag = false;
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
StaticRefPtr<BluetoothService> sBluetoothService;
|
||||
|
||||
bool sInShutdown = false;
|
||||
bool sToggleInProgress = false;
|
||||
|
||||
bool
|
||||
IsMainProcess()
|
||||
{
|
||||
return XRE_GetProcessType() == GeckoProcessType_Default;
|
||||
}
|
||||
|
||||
void
|
||||
ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
*static_cast<bool*>(aClosure) = true;
|
||||
}
|
||||
|
||||
void
|
||||
GetAllBluetoothActors(InfallibleTArray<BluetoothParent*>& aActors)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aActors.IsEmpty());
|
||||
|
||||
nsAutoTArray<ContentParent*, 20> contentActors;
|
||||
ContentParent::GetAll(contentActors);
|
||||
|
||||
for (uint32_t contentIndex = 0;
|
||||
contentIndex < contentActors.Length();
|
||||
contentIndex++) {
|
||||
MOZ_ASSERT(contentActors[contentIndex]);
|
||||
|
||||
AutoInfallibleTArray<PBluetoothParent*, 5> bluetoothActors;
|
||||
contentActors[contentIndex]->ManagedPBluetoothParent(bluetoothActors);
|
||||
|
||||
for (uint32_t bluetoothIndex = 0;
|
||||
bluetoothIndex < bluetoothActors.Length();
|
||||
bluetoothIndex++) {
|
||||
MOZ_ASSERT(bluetoothActors[bluetoothIndex]);
|
||||
|
||||
BluetoothParent* actor =
|
||||
static_cast<BluetoothParent*>(bluetoothActors[bluetoothIndex]);
|
||||
aActors.AppendElement(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled)
|
||||
: mEnabled(aEnabled)
|
||||
{ }
|
||||
|
||||
NS_METHOD
|
||||
BluetoothService::ToggleBtAck::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// This is requested in Bug 836516. With settings this property, WLAN
|
||||
// firmware could be aware of Bluetooth has been turned on/off, so that the
|
||||
// mecahnism of handling coexistence of WIFI and Bluetooth could be started.
|
||||
//
|
||||
// In the future, we may have our own way instead of setting a system
|
||||
// property to let firmware developers be able to sense that Bluetooth has
|
||||
// been toggled.
|
||||
#if defined(MOZ_WIDGET_GONK)
|
||||
if (property_set(PROP_BLUETOOTH_ENABLED, mEnabled ? "true" : "false") != 0) {
|
||||
BT_WARNING("Failed to set bluetooth enabled property");
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ENSURE_TRUE(sBluetoothService, NS_OK);
|
||||
|
||||
if (sInShutdown) {
|
||||
sBluetoothService = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Update mEnabled of BluetoothService object since
|
||||
// StartInternal/StopInternal have been already done.
|
||||
sBluetoothService->SetEnabled(mEnabled);
|
||||
sToggleInProgress = false;
|
||||
|
||||
nsAutoString signalName;
|
||||
signalName = mEnabled ? NS_LITERAL_STRING("Enabled")
|
||||
: NS_LITERAL_STRING("Disabled");
|
||||
BluetoothSignal signal(signalName, NS_LITERAL_STRING(KEY_MANAGER), true);
|
||||
sBluetoothService->DistributeSignal(signal);
|
||||
|
||||
// Event 'AdapterAdded' has to be fired after firing 'Enabled'
|
||||
sBluetoothService->TryFiringAdapterAdded();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class BluetoothService::StartupTask : public nsISettingsServiceCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!aResult.isBoolean()) {
|
||||
BT_WARNING("Setting for '" BLUETOOTH_ENABLED_SETTING "' is not a boolean!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// It is theoretically possible to shut down before the first settings check
|
||||
// has completed (though extremely unlikely).
|
||||
if (sBluetoothService) {
|
||||
return sBluetoothService->HandleStartupSettingsCheck(aResult.toBoolean());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD HandleError(const nsAString& aName)
|
||||
{
|
||||
BT_WARNING("Unable to get value for '" BLUETOOTH_ENABLED_SETTING "'");
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(BluetoothService::StartupTask, nsISettingsServiceCallback);
|
||||
|
||||
NS_IMPL_ISUPPORTS(BluetoothService, nsIObserver)
|
||||
|
||||
bool
|
||||
BluetoothService::IsToggling() const
|
||||
{
|
||||
return sToggleInProgress;
|
||||
}
|
||||
|
||||
BluetoothService::~BluetoothService()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
RemoveObserversExceptBluetoothManager
|
||||
(const nsAString& key,
|
||||
nsAutoPtr<BluetoothSignalObserverList>& value,
|
||||
void* arg)
|
||||
{
|
||||
if (!key.EqualsLiteral(KEY_MANAGER)) {
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::RemoveObserverFromTable(const nsAString& key)
|
||||
{
|
||||
mBluetoothSignalObserverTable.Remove(key);
|
||||
}
|
||||
|
||||
// static
|
||||
BluetoothService*
|
||||
BluetoothService::Create()
|
||||
{
|
||||
#if defined(MOZ_B2G_BT)
|
||||
if (!IsMainProcess()) {
|
||||
return BluetoothServiceChildProcess::Create();
|
||||
}
|
||||
|
||||
#if defined(MOZ_B2G_BT_BLUEZ)
|
||||
return new BluetoothDBusService();
|
||||
#elif defined(MOZ_B2G_BT_BLUEDROID)
|
||||
return new BluetoothServiceBluedroid();
|
||||
#endif
|
||||
#elif defined(MOZ_BLUETOOTH_DBUS)
|
||||
return new BluetoothDBusService();
|
||||
#endif
|
||||
|
||||
BT_WARNING("No platform support for bluetooth!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothService::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, false);
|
||||
|
||||
if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
|
||||
false))) {
|
||||
BT_WARNING("Failed to add shutdown observer!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only the main process should observe bluetooth settings changes.
|
||||
if (IsMainProcess() &&
|
||||
NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) {
|
||||
BT_WARNING("Failed to add settings change observer!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::Cleanup()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs &&
|
||||
(NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
|
||||
NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID)))) {
|
||||
BT_WARNING("Can't unregister observers!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::RegisterBluetoothSignalHandler(
|
||||
const nsAString& aNodeName,
|
||||
BluetoothSignalObserver* aHandler)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aHandler);
|
||||
|
||||
BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get());
|
||||
|
||||
BluetoothSignalObserverList* ol;
|
||||
if (!mBluetoothSignalObserverTable.Get(aNodeName, &ol)) {
|
||||
ol = new BluetoothSignalObserverList();
|
||||
mBluetoothSignalObserverTable.Put(aNodeName, ol);
|
||||
}
|
||||
|
||||
ol->AddObserver(aHandler);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::UnregisterBluetoothSignalHandler(
|
||||
const nsAString& aNodeName,
|
||||
BluetoothSignalObserver* aHandler)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aHandler);
|
||||
|
||||
BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get());
|
||||
|
||||
BluetoothSignalObserverList* ol;
|
||||
if (mBluetoothSignalObserverTable.Get(aNodeName, &ol)) {
|
||||
ol->RemoveObserver(aHandler);
|
||||
// We shouldn't have duplicate instances in the ObserverList, but there's
|
||||
// no appropriate way to do duplication check while registering, so
|
||||
// assertions are added here.
|
||||
MOZ_ASSERT(!ol->RemoveObserver(aHandler));
|
||||
if (ol->Length() == 0) {
|
||||
mBluetoothSignalObserverTable.Remove(aNodeName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BT_WARNING("Node was never registered!");
|
||||
}
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
RemoveAllSignalHandlers(const nsAString& aKey,
|
||||
nsAutoPtr<BluetoothSignalObserverList>& aData,
|
||||
void* aUserArg)
|
||||
{
|
||||
BluetoothSignalObserver* handler = static_cast<BluetoothSignalObserver*>(aUserArg);
|
||||
aData->RemoveObserver(handler);
|
||||
// We shouldn't have duplicate instances in the ObserverList, but there's
|
||||
// no appropriate way to do duplication check while registering, so
|
||||
// assertions are added here.
|
||||
MOZ_ASSERT(!aData->RemoveObserver(handler));
|
||||
return aData->Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aHandler);
|
||||
|
||||
mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (aSignal.path().EqualsLiteral(KEY_LOCAL_AGENT)) {
|
||||
Notify(aSignal);
|
||||
return;
|
||||
} else if (aSignal.path().EqualsLiteral(KEY_REMOTE_AGENT)) {
|
||||
Notify(aSignal);
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothSignalObserverList* ol;
|
||||
if (!mBluetoothSignalObserverTable.Get(aSignal.path(), &ol)) {
|
||||
#if DEBUG
|
||||
nsAutoCString msg("No observer registered for path ");
|
||||
msg.Append(NS_ConvertUTF16toUTF8(aSignal.path()));
|
||||
BT_WARNING(msg.get());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(ol->Length());
|
||||
ol->Broadcast(aSignal);
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StartBluetooth(bool aIsStartup)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sInShutdown) {
|
||||
// Don't try to start if we're already shutting down.
|
||||
MOZ_ASSERT(false, "Start called while in shutdown!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mAdapterAddedReceived = false;
|
||||
|
||||
/* When IsEnabled() is true, we don't switch on Bluetooth but we still
|
||||
* send ToggleBtAck task. One special case happens at startup stage. At
|
||||
* startup, the initialization of BluetoothService still has to be done
|
||||
* even if Bluetooth is already enabled.
|
||||
*
|
||||
* Please see bug 892392 for more information.
|
||||
*/
|
||||
if (aIsStartup || !sBluetoothService->IsEnabled()) {
|
||||
// Switch Bluetooth on
|
||||
if (NS_FAILED(sBluetoothService->StartInternal())) {
|
||||
BT_WARNING("Bluetooth service failed to start!");
|
||||
}
|
||||
} else {
|
||||
BT_WARNING("Bluetooth has already been enabled before.");
|
||||
nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StopBluetooth(bool aIsStartup)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothProfileManagerBase* profile;
|
||||
profile = BluetoothHfpManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
profile = BluetoothOppManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
}
|
||||
|
||||
profile = BluetoothA2dpManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
profile = BluetoothHidManager::Get();
|
||||
NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
|
||||
if (profile->IsConnected()) {
|
||||
profile->Disconnect(nullptr);
|
||||
} else {
|
||||
profile->Reset();
|
||||
}
|
||||
|
||||
mAdapterAddedReceived = false;
|
||||
|
||||
/* When IsEnabled() is false, we don't switch off Bluetooth but we still
|
||||
* send ToggleBtAck task. One special case happens at startup stage. At
|
||||
* startup, the initialization of BluetoothService still has to be done
|
||||
* even if Bluetooth is disabled.
|
||||
*
|
||||
* Please see bug 892392 for more information.
|
||||
*/
|
||||
if (aIsStartup || sBluetoothService->IsEnabled()) {
|
||||
// Switch Bluetooth off
|
||||
if (NS_FAILED(sBluetoothService->StopInternal())) {
|
||||
BT_WARNING("Bluetooth service failed to stop!");
|
||||
}
|
||||
} else {
|
||||
BT_WARNING("Bluetooth has already been enabled/disabled before.");
|
||||
nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
|
||||
{
|
||||
nsresult rv;
|
||||
if (aStart) {
|
||||
rv = StartBluetooth(aIsStartup);
|
||||
} else {
|
||||
rv = StopBluetooth(aIsStartup);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::SetEnabled(bool aEnabled)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
AutoInfallibleTArray<BluetoothParent*, 10> childActors;
|
||||
GetAllBluetoothActors(childActors);
|
||||
|
||||
for (uint32_t index = 0; index < childActors.Length(); index++) {
|
||||
unused << childActors[index]->SendEnabled(aEnabled);
|
||||
}
|
||||
|
||||
if (!aEnabled) {
|
||||
/**
|
||||
* Remove all handlers except BluetoothManager when turning off bluetooth
|
||||
* since it is possible that the event 'onAdapterAdded' would be fired after
|
||||
* BluetoothManagers of child process are registered. Please see Bug 827759
|
||||
* for more details.
|
||||
*/
|
||||
mBluetoothSignalObserverTable.Enumerate(
|
||||
RemoveObserversExceptBluetoothManager, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* mEnabled: real status of bluetooth
|
||||
* aEnabled: expected status of bluetooth
|
||||
*/
|
||||
if (mEnabled == aEnabled) {
|
||||
BT_WARNING("Bluetooth has already been enabled/disabled before "
|
||||
"or the toggling is failed.");
|
||||
}
|
||||
|
||||
mEnabled = aEnabled;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::HandleStartup()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!sToggleInProgress);
|
||||
|
||||
nsCOMPtr<nsISettingsService> settings =
|
||||
do_GetService("@mozilla.org/settingsService;1");
|
||||
NS_ENSURE_TRUE(settings, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<nsISettingsServiceLock> settingsLock;
|
||||
nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<StartupTask> callback = new StartupTask();
|
||||
rv = settingsLock->Get(BLUETOOTH_ENABLED_SETTING, callback);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
sToggleInProgress = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::HandleStartupSettingsCheck(bool aEnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return StartStopBluetooth(aEnable, true);
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::HandleSettingsChanged(const nsAString& aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// The string that we're interested in will be a JSON string that looks like:
|
||||
// {"key":"bluetooth.enabled","value":true}
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
if (!cx) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) {
|
||||
return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!val.isObject()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(cx, &val.toObject());
|
||||
|
||||
JS::Rooted<JS::Value> key(cx);
|
||||
if (!JS_GetProperty(cx, obj, "key", &key)) {
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!key.isString()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// First, check if the string equals to BLUETOOTH_DEBUGGING_SETTING
|
||||
bool match;
|
||||
if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) {
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
JS::Rooted<JS::Value> value(cx);
|
||||
if (!JS_GetProperty(cx, obj, "value", &value)) {
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!value.isBoolean()) {
|
||||
MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
SWITCH_BT_DEBUG(value.toBoolean());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Second, check if the string is BLUETOOTH_ENABLED_SETTING
|
||||
if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) {
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
JS::Rooted<JS::Value> value(cx);
|
||||
if (!JS_GetProperty(cx, obj, "value", &value)) {
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!value.isBoolean()) {
|
||||
MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (sToggleInProgress || value.toBoolean() == IsEnabled()) {
|
||||
// Nothing to do here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
sToggleInProgress = true;
|
||||
|
||||
nsresult rv = StartStopBluetooth(value.toBoolean(), false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::HandleShutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// This is a two phase shutdown. First we notify all child processes that
|
||||
// bluetooth is going away, and then we wait for them to acknowledge. Then we
|
||||
// close down all the bluetooth machinery.
|
||||
|
||||
sInShutdown = true;
|
||||
|
||||
Cleanup();
|
||||
|
||||
AutoInfallibleTArray<BluetoothParent*, 10> childActors;
|
||||
GetAllBluetoothActors(childActors);
|
||||
|
||||
if (!childActors.IsEmpty()) {
|
||||
// Notify child processes that they should stop using bluetooth now.
|
||||
for (uint32_t index = 0; index < childActors.Length(); index++) {
|
||||
childActors[index]->BeginShutdown();
|
||||
}
|
||||
|
||||
// Create a timer to ensure that we don't wait forever for a child process
|
||||
// or the bluetooth threads to finish. If we don't get a timer or can't use
|
||||
// it for some reason then we skip all the waiting entirely since we really
|
||||
// can't afford to hang on shutdown.
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
MOZ_ASSERT(timer);
|
||||
|
||||
if (timer) {
|
||||
bool timeExceeded = false;
|
||||
|
||||
if (NS_SUCCEEDED(timer->InitWithFuncCallback(ShutdownTimeExceeded,
|
||||
&timeExceeded,
|
||||
DEFAULT_SHUTDOWN_TIMER_MS,
|
||||
nsITimer::TYPE_ONE_SHOT))) {
|
||||
nsIThread* currentThread = NS_GetCurrentThread();
|
||||
MOZ_ASSERT(currentThread);
|
||||
|
||||
// Wait for those child processes to acknowledge.
|
||||
while (!timeExceeded && !childActors.IsEmpty()) {
|
||||
if (!NS_ProcessNextEvent(currentThread)) {
|
||||
MOZ_ASSERT(false, "Something horribly wrong here!");
|
||||
break;
|
||||
}
|
||||
GetAllBluetoothActors(childActors);
|
||||
}
|
||||
|
||||
if (NS_FAILED(timer->Cancel())) {
|
||||
MOZ_CRASH("Failed to cancel shutdown timer, this will crash!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
MOZ_ASSERT(false, "Failed to initialize shutdown timer!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsEnabled() && NS_FAILED(StopBluetooth(false))) {
|
||||
MOZ_ASSERT(false, "Failed to deliver stop message!");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
BluetoothService*
|
||||
BluetoothService::Get()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// If we already exist, exit early
|
||||
if (sBluetoothService) {
|
||||
return sBluetoothService;
|
||||
}
|
||||
|
||||
// If we're in shutdown, don't create a new instance
|
||||
if (sInShutdown) {
|
||||
BT_WARNING("BluetoothService can't be created during shutdown");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create new instance, register, return
|
||||
nsRefPtr<BluetoothService> service = BluetoothService::Create();
|
||||
NS_ENSURE_TRUE(service, nullptr);
|
||||
|
||||
if (!service->Init()) {
|
||||
service->Cleanup();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sBluetoothService = service;
|
||||
return sBluetoothService;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!strcmp(aTopic, "profile-after-change")) {
|
||||
return HandleStartup();
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
|
||||
return HandleSettingsChanged(nsDependentString(aData));
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
return HandleShutdown();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::TryFiringAdapterAdded()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (IsToggling() || !mAdapterAddedReceived) {
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothSignal signal(NS_LITERAL_STRING("AdapterAdded"),
|
||||
NS_LITERAL_STRING(KEY_MANAGER), true);
|
||||
DistributeSignal(signal);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::AdapterAddedReceived()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mAdapterAddedReceived = true;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::Notify(const BluetoothSignal& aData)
|
||||
{
|
||||
nsString type = NS_LITERAL_STRING("bluetooth-pairing-request");
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(),
|
||||
JS::NullPtr()));
|
||||
NS_ENSURE_TRUE_VOID(obj);
|
||||
|
||||
if (!SetJsObject(cx, aData.value(), obj)) {
|
||||
BT_WARNING("Failed to set properties of system message!");
|
||||
return;
|
||||
}
|
||||
|
||||
BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
|
||||
|
||||
if (aData.name().EqualsLiteral("RequestConfirmation")) {
|
||||
MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 4,
|
||||
"RequestConfirmation: Wrong length of parameters");
|
||||
} else if (aData.name().EqualsLiteral("RequestPinCode")) {
|
||||
MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3,
|
||||
"RequestPinCode: Wrong length of parameters");
|
||||
} else if (aData.name().EqualsLiteral("RequestPasskey")) {
|
||||
MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3,
|
||||
"RequestPinCode: Wrong length of parameters");
|
||||
} else if (aData.name().EqualsLiteral("Cancel")) {
|
||||
MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 0,
|
||||
"Cancel: Wrong length of parameters");
|
||||
type.AssignLiteral("bluetooth-cancel");
|
||||
} else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID)) {
|
||||
MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 1,
|
||||
"pairedstatuschanged: Wrong length of parameters");
|
||||
type.AssignLiteral("bluetooth-pairedstatuschanged");
|
||||
} else {
|
||||
nsCString warningMsg;
|
||||
warningMsg.AssignLiteral("Not handling service signal: ");
|
||||
warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
|
||||
BT_WARNING(warningMsg.get());
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
|
||||
do_GetService("@mozilla.org/system-message-internal;1");
|
||||
NS_ENSURE_TRUE_VOID(systemMessenger);
|
||||
|
||||
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj));
|
||||
systemMessenger->BroadcastMessage(type, value,
|
||||
JS::UndefinedHandleValue);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user