Merge m-c to fx-team.

This commit is contained in:
Ryan VanderMeulen 2014-05-21 08:56:33 -04:00
commit 7e8c73f3e6
384 changed files with 39285 additions and 2611 deletions

View File

@ -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() {

View File

@ -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) {

View File

@ -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,

View File

@ -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;
}

View File

@ -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;
},

View File

@ -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">

View File

@ -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}

View File

@ -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]);

View File

@ -10,7 +10,6 @@ EXTRA_COMPONENTS += [
'ActivitiesGlue.js',
'AlertsService.js',
'B2GAboutRedirector.js',
'ContentHandler.js',
'ContentPermissionPrompt.js',
'FilePicker.js',
'HelperAppDialog.js',

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "2f15fe97023bf41e29660a2d427bd43a3ff181b0",
"revision": "9b29b2b76fa9038d3162261c174a92dd5ef704d2",
"repo_path": "/integration/gaia-central"
}

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -6,8 +6,8 @@
"filename": "setup.sh"
},
{
"size": 165464,
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -6,8 +6,8 @@
"filename": "setup.sh"
},
{
"size": 165464,
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -15,8 +15,8 @@
"filename": "clang.tar.bz2"
},
{
"size": 165464,
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -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"/>

View File

@ -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;
}

View File

@ -12,8 +12,8 @@
"filename": "gcc.tar.xz"
},
{
"size": 165464,
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -12,8 +12,8 @@
"filename": "gcc.tar.xz"
},
{
"size": 165464,
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -15,8 +15,8 @@
"filename": "clang.tar.bz2"
},
{
"size": 165464,
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -12,8 +12,8 @@
"filename": "setup.sh"
},
{
"size": 165464,
"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -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);

View File

@ -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]

View File

@ -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;
});

View File

@ -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>

View File

@ -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();
}
},
/**

View File

@ -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

View File

@ -20,3 +20,5 @@ GENERATED_SOURCES += [
DEFINES['ELFHACK_BUILD'] = True
NO_PGO = True
NO_VISIBILITY_FLAGS = True

View 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)

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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___ */

View File

@ -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:

View File

@ -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)

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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());
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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()) {

View File

@ -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;
}

View File

@ -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

View File

@ -779,6 +779,7 @@ bool OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aTimeUs,
if (aDoSeek) {
{
Mutex::Autolock autoLock(mSeekLock);
ReleaseAllPendingVideoBuffersLocked();
mIsVideoSeeking = true;
}
MediaSource::ReadOptions options;

View File

@ -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

View File

@ -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.

View File

@ -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>

View File

@ -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,

View 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

View File

@ -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]

View 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>

View File

@ -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;
}
);
}
}

View File

@ -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();
}

View 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]);

View 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}

View File

@ -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 += [

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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.

View 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;
};

View File

@ -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

View File

@ -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();

View File

@ -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
{

View File

@ -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]

View File

@ -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>

File diff suppressed because it is too large Load Diff

View 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

View 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__

View 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);
}

View 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

View 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__

View 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)

View 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__

View 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);
}

View 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

View 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();
}
}

View 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

View 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__

View 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();
}

View 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

View 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()
{}

View 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

View 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;
}

View 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

View 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