Merge b2g-inbound to m-c.

This commit is contained in:
Ryan VanderMeulen 2013-08-09 17:26:14 -04:00
commit 1c27efb8bd
77 changed files with 2245 additions and 505 deletions

View File

@ -17,6 +17,7 @@ html xul|scrollbar {
background-image: none !important;
border: 0px solid transparent !important;
z-index: 2147483647;
pointer-events: none;
opacity: 1;
}

View File

@ -791,6 +791,7 @@ var AlertsHelper = {
if (!manifestUrl || !manifestUrl.length) {
send(null, null);
return;
}
// If we have a manifest URL, get the icon and title from the manifest

View File

@ -40,7 +40,8 @@ category app-startup ProcessGlobal service,@mozilla.org/b2g-process-global;1
# ContentHandler.js
component {d18d0216-d50c-11e1-ba54-efb18d0ef0ac} ContentHandler.js
contract @mozilla.org/uriloader/content-handler;1?type=application/pdf {d18d0216-d50c-11e1-ba54-efb18d0ef0ac}
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

View File

@ -18,22 +18,28 @@ XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
.getService(Ci.nsIMessageSender);
});
function log(aMsg) {
let msg = "ContentHandler.js: " + (aMsg.join ? aMsg.join("") : aMsg);
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
.logStringMessage(msg);
dump(msg + "\n");
function debug(aMsg) {
//dump("--*-- ContentHandler: " + aMsg + "\n");
}
const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
function ContentHandler() {
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])
}
ContentHandler.prototype = {
handleContent: function handleContent(aMimetype, aContext, aRequest) {
if (aMimetype != PDF_CONTENT_TYPE)
throw NS_ERROR_WONT_HANDLE_CONTENT;
function ActivityContentHandler() {
}
ActivityContentHandler.prototype = {
handleContent: function handleContent(aMimetype, aContext, aRequest) {
if (!(aRequest instanceof Ci.nsIChannel))
throw NS_ERROR_WONT_HANDLE_CONTENT;
@ -46,8 +52,96 @@ ContentHandler.prototype = {
aRequest.cancel(Cr.NS_BINDING_ABORTED);
},
classID: Components.ID("{d18d0216-d50c-11e1-ba54-efb18d0ef0ac}"),
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

@ -188,7 +188,9 @@ ContentPermissionPrompt.prototype = {
this.sendToBrowserWindow("permission-prompt", request, requestId, function(type, remember) {
if (type == "permission-allow") {
rememberPermission(request.type, principal, !remember);
callback();
if (callback) {
callback();
}
request.allow();
return;
}
@ -202,7 +204,9 @@ ContentPermissionPrompt.prototype = {
Ci.nsIPermissionManager.EXPIRE_SESSION, 0);
}
callback();
if (callback) {
callback();
}
request.cancel();
});
},

View File

@ -106,7 +106,7 @@ let Keyboard = {
switch (msg.name) {
case 'Forms:Input':
this.forwardEvent('Keyboard:FocusChange', msg);
this.handleFocusChange(msg);
break;
case 'Forms:SelectionChange':
case 'Forms:GetText:Result:OK':
@ -163,6 +163,17 @@ let Keyboard = {
ppmm.broadcastAsyncMessage(newEventName, msg.data);
},
handleFocusChange: function keyboardHandleFocusChange(msg) {
this.forwardEvent('Keyboard:FocusChange', msg);
let browser = Services.wm.getMostRecentWindow("navigator:browser");
browser.shell.sendChromeEvent({
type: 'inputmethod-contextchange',
inputType: msg.data.type
});
},
setSelectedOption: function keyboardSetSelectedOption(msg) {
this.messageManager.sendAsyncMessage('Forms:Select:Choice', msg.data);
},
@ -191,14 +202,14 @@ let Keyboard = {
showInputMethodPicker: function keyboardShowInputMethodPicker() {
let browser = Services.wm.getMostRecentWindow("navigator:browser");
browser.shell.sendChromeEvent({
type: "input-method-show-picker"
type: "inputmethod-showall"
});
},
switchToNextInputMethod: function keyboardSwitchToNextInputMethod() {
let browser = Services.wm.getMostRecentWindow("navigator:browser");
browser.shell.sendChromeEvent({
type: "input-method-switch-to-next"
type: "inputmethod-next"
});
},

View File

@ -1,4 +1,4 @@
{
"revision": "fd03fbd18a09517bc5eb4e2af62314421ae7124a",
"revision": "0e3e30e489ff38cceb8b9cf9ee5caea5fb072457",
"repo_path": "/integration/gaia-central"
}

View File

@ -50,9 +50,7 @@ this.webappsUI = {
_getWindowForId: function(aId) {
let someWindow = Services.wm.getMostRecentWindow(null);
return someWindow && someWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.getOuterWindowWithId(aId);
return someWindow && Services.wm.getOuterWindowWithId(aId);
},
openURL: function(aUrl, aOrigin) {

View File

@ -180,7 +180,7 @@ interface nsIMessageListener : nsISupports
void receiveMessage();
};
[scriptable, builtinclass, uuid(9c37a142-3de3-4902-a1a4-133f37d5980a)]
[scriptable, builtinclass, uuid(aae827bd-acf1-45fe-a556-ea545d4c0804)]
interface nsIMessageListenerManager : nsISupports
{
/**
@ -197,12 +197,31 @@ interface nsIMessageListenerManager : nsISupports
in nsIMessageListener listener);
/**
* No longer invoke |listener| when |messageName| is received, after
* the first time removeMessageListener() is called.
* Undo an |addMessageListener| call -- that is, calling this causes us to no
* longer invoke |listener| when |messageName| is received.
*
* removeMessageListener does not remove a message listener added via
* addWeakMessageListener; use removeWeakMessageListener for that.
*/
void removeMessageListener(in AString messageName,
in nsIMessageListener listener);
/**
* This is just like addMessageListener, except the message manager holds a
* weak ref to |listener|.
*
* If you have two weak message listeners for the same message, they may be
* called in any order.
*/
void addWeakMessageListener(in AString messageName,
in nsIMessageListener listener);
/**
* This undoes an |addWeakMessageListener| call.
*/
void removeWeakMessageListener(in AString messageName,
in nsIMessageListener listener);
[notxpcom] boolean markForCC();
};

View File

@ -52,8 +52,8 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
uint32_t count = tmp->mListeners.Length();
for (uint32_t i = 0; i < count; i++) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mListener");
cb.NoteXPCOMChild(tmp->mListeners[i].mListener.get());
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mStrongListener");
cb.NoteXPCOMChild(tmp->mListeners[i].mStrongListener.get());
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -247,14 +247,14 @@ nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
uint32_t len = mListeners.Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mMessage == message &&
mListeners[i].mListener == aListener) {
mListeners[i].mStrongListener == aListener) {
return NS_OK;
}
}
nsMessageListenerInfo* entry = mListeners.AppendElement();
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
entry->mMessage = message;
entry->mListener = aListener;
entry->mStrongListener = aListener;
return NS_OK;
}
@ -266,7 +266,7 @@ nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
uint32_t len = mListeners.Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mMessage == message &&
mListeners[i].mListener == aListener) {
mListeners[i].mStrongListener == aListener) {
mListeners.RemoveElementAt(i);
return NS_OK;
}
@ -274,6 +274,66 @@ nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
return NS_OK;
}
NS_IMETHODIMP
nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
nsIMessageListener* aListener)
{
nsWeakPtr weak = do_GetWeakReference(aListener);
NS_ENSURE_TRUE(weak, NS_ERROR_NO_INTERFACE);
#ifdef DEBUG
// It's technically possible that one object X could give two different
// nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want
// this to happen; it will break e.g. RemoveWeakMessageListener. So let's
// check that we're not getting ourselves into that situation.
nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
if (!mListeners[i].mWeakListener) {
continue;
}
nsCOMPtr<nsISupports> otherCanonical =
do_QueryReferent(mListeners[i].mWeakListener);
MOZ_ASSERT((canonical == otherCanonical) ==
(weak == mListeners[i].mWeakListener));
}
#endif
nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
uint32_t len = mListeners.Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mMessage == message &&
mListeners[i].mWeakListener == weak) {
return NS_OK;
}
}
nsMessageListenerInfo* entry = mListeners.AppendElement();
entry->mMessage = message;
entry->mWeakListener = weak;
return NS_OK;
}
NS_IMETHODIMP
nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage,
nsIMessageListener* aListener)
{
nsWeakPtr weak = do_GetWeakReference(aListener);
NS_ENSURE_TRUE(weak, NS_OK);
nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
uint32_t len = mListeners.Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mMessage == message &&
mListeners[i].mWeakListener == weak) {
mListeners.RemoveElementAt(i);
return NS_OK;
}
}
return NS_OK;
}
// nsIFrameScriptLoader
NS_IMETHODIMP
@ -690,14 +750,30 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
InfallibleTArray<nsString>* aJSONRetVal)
{
AutoSafeJSContext ctx;
if (mListeners.Length()) {
nsCOMPtr<nsIAtom> name = do_GetAtom(aMessage);
MMListenerRemover lr(this);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
// Remove mListeners[i] if it's an expired weak listener.
nsCOMPtr<nsIMessageListener> weakListener;
if (mListeners[i].mWeakListener) {
weakListener = do_QueryReferent(mListeners[i].mWeakListener);
if (!weakListener) {
mListeners.RemoveElementAt(i--);
continue;
}
}
if (mListeners[i].mMessage == name) {
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
do_QueryInterface(mListeners[i].mListener);
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
if (weakListener) {
wrappedJS = do_QueryInterface(weakListener);
} else {
wrappedJS = do_QueryInterface(mListeners[i].mStrongListener);
}
if (!wrappedJS) {
continue;
}
@ -1428,7 +1504,9 @@ nsFrameMessageManager::MarkForCC()
{
uint32_t len = mListeners.Length();
for (uint32_t i = 0; i < len; ++i) {
xpc_TryUnmarkWrappedGrayObject(mListeners[i].mListener);
if (mListeners[i].mStrongListener) {
xpc_TryUnmarkWrappedGrayObject(mListeners[i].mStrongListener);
}
}
if (mRefCnt.IsPurple()) {
mRefCnt.RemovePurple();

View File

@ -21,6 +21,7 @@
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
#include "nsWeakPtr.h"
#include "mozilla/Attributes.h"
namespace mozilla {
@ -111,7 +112,9 @@ class JSObject;
struct nsMessageListenerInfo
{
nsCOMPtr<nsIMessageListener> mListener;
// Exactly one of mStrongListener and mWeakListener must be non-null.
nsCOMPtr<nsIMessageListener> mStrongListener;
nsWeakPtr mWeakListener;
nsCOMPtr<nsIAtom> mMessage;
};

View File

@ -11,14 +11,30 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682
<label value="Mozilla Bug 549682"/>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var Cu = Components.utils;
var didRunAsync = false;
var didRunLocal = false;
var global = Components.classes["@mozilla.org/globalmessagemanager;1"]
.getService(Components.interfaces.nsIMessageBroadcaster);
var ppm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Components.interfaces.nsIMessageBroadcaster);
var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
.getService(Components.interfaces.nsISyncMessageSender);
var global = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Components.interfaces.nsIMessageBroadcaster);
var ppm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Components.interfaces.nsIMessageBroadcaster);
var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Components.interfaces.nsISyncMessageSender);
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function ok(cond, msg) {
opener.wrappedJSObject.ok(cond, msg);
}
function is(actual, expected, msg) {
opener.wrappedJSObject.is(actual, expected, msg);
}
var asyncPPML = false;
function ppmASL(m) {
@ -42,9 +58,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682
ppm.broadcastAsyncMessage("childprocessmessage", "");
function checkPMMMessages() {
opener.wrappedJSObject.ok(asyncPPML, "should have handled async message");
opener.wrappedJSObject.ok(syncPPML, "should have handled sync message");
opener.wrappedJSObject.ok(asyncCPML, "should have handled async message");
ok(asyncPPML, "should have handled async message");
ok(syncPPML, "should have handled sync message");
ok(asyncCPML, "should have handled async message");
ppm.removeMessageListener("processmessageAsync", ppmASL);
ppm.removeMessageListener("processmessageSync", ppmSL);
cpm.removeMessageListener("childprocessmessage", cpmASL);
@ -58,38 +74,77 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682
global.removeMessageListener("sync", globalListener);
global.removeMessageListener("global-sync", globalListener);
// Note, the result depends on what other windows are open.
opener.wrappedJSObject.ok(globalListenerCallCount >= 4,
"Global listener should have been called at least 4 times!");
opener.wrappedJSObject.ok(didRunLocal, "Local message received.");
opener.setTimeout("done()", 0);
var i = document.getElementById("ifr");
i.parentNode.removeChild(i); // This is a crash test!
window.close();
ok(globalListenerCallCount >= 4,
"Global listener should have been called at least 4 times!");
ok(didRunLocal, "Local message received.");
}
}
function asyncL(m) {
didRunAsync = true;
opener.wrappedJSObject.is(m.name, "async", "Wrong message!");
opener.wrappedJSObject.is(m.json.data, 1234, "Wrong data!");
is(m.name, "async", "Wrong message!");
is(m.json.data, 1234, "Wrong data!");
}
function syncL(m) {
opener.wrappedJSObject.is(m.name, "sync", "Wrong message!");
opener.wrappedJSObject.is(m.json.data, 1234, "Wrong data!");
opener.wrappedJSObject.ok(didRunAsync, "Should have run async!");
is(m.name, "sync", "Wrong message!");
is(m.json.data, 1234, "Wrong data!");
ok(didRunAsync, "Should have run async!");
}
function localL(m) {
opener.wrappedJSObject.is(m.name, "lasync", "Wrong message!");
opener.wrappedJSObject.is(m.json.data, 2345, "Wrong data!");
is(m.name, "lasync", "Wrong message!");
is(m.json.data, 2345, "Wrong data!");
didRunLocal = true;
}
var weakMessageReceived = false;
var weakListener = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
Ci.nsISupportsWeakReference]),
receiveMessage: function(msg) {
if (weakMessageReceived) {
ok(false, 'Weak listener fired twice.');
return;
}
ok(true, 'Weak listener fired once.');
weakMessageReceived = true;
document.getElementById('ifr').messageManager
.removeWeakMessageListener('weak', weakListener);
}
};
var weakListener2 = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
Ci.nsISupportsWeakReference]),
receiveMessage: function(msg) {
ok(false, 'Should not have received a message.');
}
};
function weakDoneListener() {
ok(weakMessageReceived, 'Got "weak" message.');
finish();
}
function finish() {
opener.setTimeout("done()", 0);
var i = document.getElementById("ifr");
i.parentNode.removeChild(i); // This is a crash test!
window.close();
}
function loadScript() {
// Async should be processed first!
messageManager.loadFrameScript("data:,sendAsyncMessage('async', { data: 1234 }); sendSyncMessage('sync', { data: 1234 });", false);
messageManager.loadFrameScript("data:,\
sendAsyncMessage('async', { data: 1234 });\
sendSyncMessage('sync', { data: 1234 });\
sendAsyncMessage('weak', {});\
sendAsyncMessage('weak', {});\
sendAsyncMessage('weakdone', {});", false);
}
function run() {
@ -97,9 +152,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682
var wn = document.getElementById('ifr').contentWindow
.getInterface(Components.interfaces.nsIWebNavigation);
opener.wrappedJSObject.ok(wn, "Should have webnavigation");
ok(wn, "Should have webnavigation");
var cfmm = wn.getInterface(Components.interfaces.nsIContentFrameMessageManager);
opener.wrappedJSObject.ok(cfmm, "Should have content messageManager");
ok(cfmm, "Should have content messageManager");
var didGetSyncMessage = false;
function syncContinueTestFn() {
@ -108,7 +163,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682
localmm.addMessageListener("syncContinueTest", syncContinueTestFn);
cfmm.sendSyncMessage("syncContinueTest", {});
localmm.removeMessageListener("syncContinueTest", syncContinueTestFn);
opener.wrappedJSObject.ok(didGetSyncMessage, "Should have got sync message!");
ok(didGetSyncMessage, "Should have got sync message!");
localmm.addMessageListener("lasync", localL);
localmm.loadFrameScript("data:,sendAsyncMessage('lasync', { data: 2345 })", false);
@ -123,7 +178,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682
var c = 0;
messageManager.addMessageListener("toberemoved", function() {
++c;
opener.wrappedJSObject.is(c, 1, "Should be called only once!");
is(c, 1, "Should be called only once!");
});
// This loads the script in the existing <browser>
messageManager.loadFrameScript(toBeRemovedScript, true);
@ -134,11 +189,36 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682
var b = document.createElement("browser");
b.setAttribute("type", "content");
document.documentElement.appendChild(b);
opener.wrappedJSObject.is(globalListenerCallCount, oldValue + 1,
is(globalListenerCallCount, oldValue + 1,
"Wrong message count");
setTimeout(checkPMMMessages, 0);
setTimeout(loadScript, 0);
localmm.addWeakMessageListener('weak', weakListener);
localmm.addMessageListener('weakdone', weakDoneListener);
// Add weakListener2 as a weak message listener, then force weakListener2
// to be gc'ed. weakListener2 shouldn't be run.
var weakRef = Cu.getWeakReference(weakListener2);
localmm.addWeakMessageListener('weak', weakListener2);
weakListener2 = null;
// Force a gc/cc in a loop until weakRef's referent has gone away.
function waitForWeakRefToDie() {
if (weakRef.get()) {
var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
.getService(Ci.nsIMemoryReporterManager);
mgr.minimizeMemoryUsage(waitForWeakRefToDie);
// Print a message so that if the test hangs in a minimizeMemoryUsage
// loop, we'll be able to see it in the log.
ok(true, "waitForWeakRefToDie spinning...");
return;
}
setTimeout(checkPMMMessages, 0);
setTimeout(loadScript, 0);
}
waitForWeakRefToDie();
}
]]></script>

View File

@ -72,6 +72,11 @@ public:
// Set the duration of the media in microseconds.
virtual void SetMediaDuration(int64_t aDuration) = 0;
// Sets the duration of the media in microseconds. The MediaDecoder
// fires a durationchange event to its owner (e.g., an HTML audio
// tag).
virtual void UpdateMediaDuration(int64_t aDuration) = 0;
// Set the media as being seekable or not.
virtual void SetMediaSeekable(bool aMediaSeekable) = 0;

View File

@ -632,7 +632,7 @@ nsresult MediaDecoder::Seek(double aTime)
if (distanceLeft == distanceRight) {
distanceLeft = Abs(leftBound - mCurrentTime);
distanceRight = Abs(rightBound - mCurrentTime);
}
}
aTime = (distanceLeft < distanceRight) ? leftBound : rightBound;
} else {
// Seek target is after the end last range in seekable data.
@ -1272,6 +1272,12 @@ void MediaDecoder::SetMediaDuration(int64_t aDuration)
GetStateMachine()->SetDuration(aDuration);
}
void MediaDecoder::UpdateMediaDuration(int64_t aDuration)
{
NS_ENSURE_TRUE_VOID(GetStateMachine());
GetStateMachine()->UpdateDuration(aDuration);
}
void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());

View File

@ -506,6 +506,7 @@ public:
virtual void SetDuration(double aDuration);
void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
void UpdateMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
// Set a flag indicating whether seeking is supported
virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;

View File

@ -456,12 +456,12 @@ public:
int64_t aStartTime,
int64_t aEndTime,
int64_t aCurrentTime) = 0;
// Called when the decode thread is started, before calling any other
// decode, read metadata, or seek functions. Do any thread local setup
// in this function.
virtual void OnDecodeThreadStart() {}
// Called when the decode thread is about to finish, after all calls to
// any other decode, read metadata, or seek functions. Any backend specific
// thread local tear down must be done in this function. Note that another
@ -519,8 +519,8 @@ public:
return functor.mResult;
}
// Only used by WebMReader for now, so stub here rather than in every
// reader than inherits from MediaDecoderReader.
// Only used by WebMReader and MediaOmxReader for now, so stub here rather
// than in every reader than inherits from MediaDecoderReader.
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }

View File

@ -140,8 +140,8 @@ private:
{
MOZ_COUNT_CTOR(StateMachineTracker);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
}
}
~StateMachineTracker()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -156,7 +156,7 @@ public:
// access always occurs after this and uses the monitor to
// safely access the decode thread counts.
static StateMachineTracker& Instance();
// Instantiate the global state machine thread if required.
// Call on main thread only.
void EnsureGlobalStateMachine();
@ -244,7 +244,7 @@ StateMachineTracker& StateMachineTracker::Instance()
return *sInstance;
}
void StateMachineTracker::EnsureGlobalStateMachine()
void StateMachineTracker::EnsureGlobalStateMachine()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
ReentrantMonitorAutoEnter mon(mMonitor);
@ -451,7 +451,7 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
mTimer->Cancel();
mTimer = nullptr;
mReader = nullptr;
StateMachineTracker::Instance().CleanupGlobalStateMachine();
#ifdef XP_WIN
timeEndPeriod(1);
@ -490,7 +490,7 @@ void MediaDecoderStateMachine::DecodeThreadRun()
{
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
mReader->OnDecodeThreadStart();
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@ -531,7 +531,7 @@ void MediaDecoderStateMachine::DecodeThreadRun()
mDecodeThreadIdle = true;
LOG(PR_LOG_DEBUG, ("%p Decode thread finished", mDecoder.get()));
}
mReader->OnDecodeThreadFinish();
}
@ -1323,7 +1323,7 @@ void MediaDecoderStateMachine::StartPlayback()
NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
if (NS_FAILED(StartAudioThread())) {
NS_WARNING("Failed to create audio thread");
NS_WARNING("Failed to create audio thread");
}
mDecoder->GetReentrantMonitor().NotifyAll();
}
@ -1446,6 +1446,16 @@ void MediaDecoderStateMachine::SetDuration(int64_t aDuration)
}
}
void MediaDecoderStateMachine::UpdateDuration(int64_t aDuration)
{
if (aDuration != GetDuration()) {
SetDuration(aDuration);
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
}
void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime)
{
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread");
@ -1700,7 +1710,7 @@ MediaDecoderStateMachine::ScheduleDecodeThread()
{
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
mStopDecodeThread = false;
if (mState >= DECODER_STATE_COMPLETED) {
return NS_OK;
@ -1831,7 +1841,7 @@ int64_t MediaDecoderStateMachine::GetUndecodedData() const
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
"Must have loaded metadata for GetBuffered() to work");
TimeRanges buffered;
nsresult res = mDecoder->GetBuffered(&buffered);
NS_ENSURE_SUCCESS(res, 0);
double currentTime = GetCurrentTime();
@ -2201,7 +2211,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
// Ensure we have a decode thread to decode metadata.
return ScheduleDecodeThread();
}
case DECODER_STATE_DECODING: {
if (mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING &&
IsPlaying())

View File

@ -157,6 +157,10 @@ public:
// aEndTime is in microseconds.
void SetMediaEndTime(int64_t aEndTime);
// Called from decode thread to update the duration. Can result in
// a durationchangeevent. aDuration is in microseconds.
void UpdateDuration(int64_t aDuration);
// Functions used by assertions to ensure we're calling things
// on the appropriate threads.
bool OnDecodeThread() const {

View File

@ -89,6 +89,7 @@ public:
NS_IMETHODIMP Run()
{
MOZ_ASSERT(NS_IsMainThread());
mRecorder->mState = RecordingState::Inactive;
mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
mRecorder->mReadThread->Shutdown();
mRecorder->mReadThread = nullptr;
@ -114,6 +115,9 @@ private:
MediaRecorder::~MediaRecorder()
{
if (mStreamPort) {
mStreamPort->Destroy();
}
if (mTrackUnionStream) {
mTrackUnionStream->Destroy();
}
@ -163,6 +167,11 @@ MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
return;
}
if (mStream->GetStream()->IsFinished() || mStream->GetStream()->IsDestroyed()) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (aTimeSlice.WasPassed()) {
if (aTimeSlice.Value() < 0) {
aResult.Throw(NS_ERROR_INVALID_ARG);
@ -191,8 +200,7 @@ MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
MOZ_ASSERT(mEncoder, "CreateEncoder failed");
mTrackUnionStream->SetAutofinish(true);
nsRefPtr<MediaInputPort> port =
mTrackUnionStream->AllocateInputPort(mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
mStreamPort = mTrackUnionStream->AllocateInputPort(mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
if (mEncoder) {
mTrackUnionStream->AddListener(mEncoder);
@ -221,7 +229,6 @@ MediaRecorder::Stop(ErrorResult& aResult)
return;
}
mTrackUnionStream->RemoveListener(mEncoder);
mState = RecordingState::Inactive;
}
void

View File

@ -110,6 +110,8 @@ protected:
nsRefPtr<DOMMediaStream> mStream;
// This media stream is used for notifying raw data to encoder and can be blocked.
nsRefPtr<ProcessedMediaStream> mTrackUnionStream;
// This is used for destroing the inputport when destroy the mediaRecorder
nsRefPtr<MediaInputPort> mStreamPort;
// This object creates on start() and destroys in ~MediaRecorder.
nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
// It specifies the container format as well as the audio and video capture formats.

View File

@ -0,0 +1,487 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 <algorithm>
#include "nsMemory.h"
#include "MP3FrameParser.h"
namespace mozilla {
// An ID3Buffer contains data of an ID3v2 header. The supplied buffer must
// point to an ID3 header and at least the size of ID_HEADER_LENGTH. Run the
// Parse method to read in the header's values.
class ID3Buffer
{
public:
enum {
ID3_HEADER_LENGTH = 10
};
ID3Buffer(const uint8_t* aBuffer, uint32_t aLength)
: mBuffer(aBuffer),
mLength(aLength),
mSize(0)
{
MOZ_ASSERT(mBuffer || !mLength);
}
nsresult Parse();
int64_t GetMP3Offset() const {
return ID3_HEADER_LENGTH + mSize;
}
private:
const uint8_t* mBuffer;
uint32_t mLength;
uint32_t mSize;
};
nsresult ID3Buffer::Parse()
{
NS_ENSURE_TRUE(mBuffer && mLength >= ID3_HEADER_LENGTH, NS_ERROR_INVALID_ARG);
if ((mBuffer[0] != 'I') ||
(mBuffer[1] != 'D') ||
(mBuffer[2] != '3') ||
(mBuffer[6] & 0x80) ||
(mBuffer[7] & 0x80) ||
(mBuffer[8] & 0x80) ||
(mBuffer[9] & 0x80)) {
return NS_ERROR_INVALID_ARG;
}
mSize = ((static_cast<uint32_t>(mBuffer[6])<<21) |
(static_cast<uint32_t>(mBuffer[7])<<14) |
(static_cast<uint32_t>(mBuffer[8])<<7) |
static_cast<uint32_t>(mBuffer[9]));
return NS_OK;
}
// The MP3Buffer contains MP3 frame data. The supplied buffer must point
// to a frame header. Call the method Parse to extract information from
// the MP3 frame headers in the supplied buffer.
class MP3Buffer
{
public:
enum {
MP3_HEADER_LENGTH = 4,
MP3_FRAMESIZE_CONST = 144000,
MP3_DURATION_CONST = 8000
};
MP3Buffer(const uint8_t* aBuffer, uint32_t aLength)
: mBuffer(aBuffer),
mLength(aLength),
mDurationUs(0),
mNumFrames(0),
mBitRateSum(0),
mFrameSizeSum(0),
mTrailing(0)
{
MOZ_ASSERT(mBuffer || !mLength);
}
static const uint8_t* FindNextHeader(const uint8_t* aBuffer, uint32_t aLength);
nsresult Parse();
int64_t GetDuration() const {
return mDurationUs;
}
int64_t GetNumberOfFrames() const {
return mNumFrames;
}
int64_t GetBitRateSum() const {
return mBitRateSum;
}
int64_t GetFrameSizeSum() const {
return mFrameSizeSum;
}
int64_t GetTrailing() const {
return mTrailing;
}
private:
enum MP3FrameHeaderField {
MP3_HDR_FIELD_SYNC,
MP3_HDR_FIELD_VERSION,
MP3_HDR_FIELD_LAYER,
MP3_HDR_FIELD_BITRATE,
MP3_HDR_FIELD_SAMPLERATE,
MP3_HDR_FIELD_PADDING,
MP3_HDR_FIELDS // Must be last enumerator value
};
enum {
MP3_HDR_CONST_FRAMESYNC = 0x7ff,
MP3_HDR_CONST_VERSION = 3,
MP3_HDR_CONST_LAYER = 1
};
static uint32_t ExtractBits(uint32_t aValue, uint32_t aOffset,
uint32_t aBits);
static uint32_t ExtractFrameHeaderField(uint32_t aHeader,
enum MP3FrameHeaderField aField);
static uint32_t ExtractFrameHeader(const uint8_t* aBuffer);
static nsresult DecodeFrameHeader(const uint8_t* aBuffer,
size_t* aFrameSize,
uint32_t* aBitRate,
uint64_t* aDuration);
static const uint16_t sBitRate[16];
static const uint16_t sSampleRate[4];
const uint8_t* mBuffer;
uint32_t mLength;
// The duration of this parsers data in milliseconds.
int64_t mDurationUs;
// The number of frames in the range.
int64_t mNumFrames;
// The sum of all frame's bit rates.
int64_t mBitRateSum;
// The sum of all frame's sizes in byte.
int32_t mFrameSizeSum;
// The number of trailing bytes.
int32_t mTrailing;
};
const uint16_t MP3Buffer::sBitRate[16] = {
0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0
};
const uint16_t MP3Buffer::sSampleRate[4] = {
44100, 48000, 32000, 0
};
uint32_t MP3Buffer::ExtractBits(uint32_t aValue, uint32_t aOffset, uint32_t aBits)
{
return (aValue >> aOffset) & ((0x1ul << aBits) - 1);
}
uint32_t MP3Buffer::ExtractFrameHeaderField(uint32_t aHeader, enum MP3FrameHeaderField aField)
{
static const uint8_t sField[MP3_HDR_FIELDS][2] = {
{21, 11}, {19, 2}, {17, 2}, {12, 4}, {10, 2}, {9, 1}
};
MOZ_ASSERT(aField < MP3_HDR_FIELDS);
return ExtractBits(aHeader, sField[aField][0], sField[aField][1]);
}
uint32_t MP3Buffer::ExtractFrameHeader(const uint8_t* aBuffer)
{
MOZ_ASSERT(aBuffer);
uint32_t header = (static_cast<uint32_t>(aBuffer[0])<<24) |
(static_cast<uint32_t>(aBuffer[1])<<16) |
(static_cast<uint32_t>(aBuffer[2])<<8) |
static_cast<uint32_t>(aBuffer[3]);
uint32_t frameSync = ExtractFrameHeaderField(header, MP3_HDR_FIELD_SYNC);
uint32_t version = ExtractFrameHeaderField(header, MP3_HDR_FIELD_VERSION);
uint32_t layer = ExtractFrameHeaderField(header, MP3_HDR_FIELD_LAYER);
uint32_t bitRate = sBitRate[ExtractFrameHeaderField(header, MP3_HDR_FIELD_BITRATE)];
uint32_t sampleRate = sSampleRate[ExtractFrameHeaderField(header, MP3_HDR_FIELD_SAMPLERATE)];
// branch-less implementation of
//
// if (fields-are-valid)
// return header;
// else
// return 0;
//
return (frameSync == uint32_t(MP3_HDR_CONST_FRAMESYNC)) *
(version == uint32_t(MP3_HDR_CONST_VERSION)) *
(layer == uint32_t(MP3_HDR_CONST_LAYER)) * !!bitRate * !!sampleRate * header;
}
const uint8_t* MP3Buffer::FindNextHeader(const uint8_t* aBuffer, uint32_t aLength)
{
MOZ_ASSERT(aBuffer || !aLength);
// Find MP3's frame-sync marker while there are at least 4 bytes
// left to contain the MP3 frame header
while (aLength >= MP3_HEADER_LENGTH) {
if (ExtractFrameHeader(aBuffer)) {
break;
}
++aBuffer;
--aLength;
}
return aBuffer;
}
nsresult MP3Buffer::DecodeFrameHeader(const uint8_t* aBuffer,
uint32_t* aFrameSize,
uint32_t* aBitRate,
uint64_t* aDuration)
{
uint32_t header = ExtractFrameHeader(aBuffer);
if (!header) {
return NS_ERROR_INVALID_ARG;
}
uint32_t bitRate = sBitRate[ExtractFrameHeaderField(header, MP3_HDR_FIELD_BITRATE)];
uint32_t sampleRate = sSampleRate[ExtractFrameHeaderField(header, MP3_HDR_FIELD_SAMPLERATE)];
uint32_t padding = ExtractFrameHeaderField(header, MP3_HDR_FIELD_PADDING);
uint32_t frameSize = (uint64_t(MP3_FRAMESIZE_CONST) * bitRate) / sampleRate + padding;
MOZ_ASSERT(aBitRate);
*aBitRate = bitRate;
MOZ_ASSERT(aFrameSize);
*aFrameSize = frameSize;
MOZ_ASSERT(aDuration);
*aDuration = (uint64_t(MP3_DURATION_CONST) * frameSize) / bitRate;
return NS_OK;
}
nsresult MP3Buffer::Parse()
{
// We walk over the newly arrived data and sum up the
// bit rates, sizes, durations, etc. of the contained
// MP3 frames.
const uint8_t* buffer = mBuffer;
uint32_t length = mLength;
while (length >= MP3_HEADER_LENGTH) {
uint32_t frameSize;
uint32_t bitRate;
uint64_t duration;
nsresult rv = DecodeFrameHeader(buffer, &frameSize, &bitRate, &duration);
NS_ENSURE_SUCCESS(rv, rv);
mBitRateSum += bitRate;
mDurationUs += duration;
++mNumFrames;
mFrameSizeSum += frameSize;
if (frameSize <= length) {
length -= frameSize;
} else {
length = 0;
}
buffer += frameSize;
}
mTrailing = length;
return NS_OK;
}
MP3FrameParser::MP3FrameParser(int64_t aLength)
: mBufferLength(0),
mLock("MP3FrameParser.mLock"),
mDurationUs(0),
mBitRateSum(0),
mNumFrames(0),
mOffset(0),
mUnhandled(0),
mLength(aLength),
mTrailing(0),
mIsMP3(true)
{ }
size_t MP3FrameParser::ParseInternalBuffer(const uint8_t* aBuffer, uint32_t aLength, int64_t aOffset)
{
if (mOffset != aOffset) {
// If we don't append, we throw away our temporary buffer.
mBufferLength = 0;
return 0;
}
size_t copyLength = 0;
if (mBufferLength || !mOffset) {
// We have some data in our temporary buffer and append to it, or
// we are at the beginning of the stream. We both cases, we append
// some data to our temporary buffer and try to parse it.
copyLength = std::min<size_t>(NS_ARRAY_LENGTH(mBuffer)-mBufferLength, aLength);
memcpy(mBuffer+mBufferLength, aBuffer, copyLength*sizeof(*mBuffer));
mBufferLength += copyLength;
}
if ((mBufferLength >= ID3Buffer::ID3_HEADER_LENGTH) && (mOffset < ID3Buffer::ID3_HEADER_LENGTH)) {
// There might be an ID3 header at the very beginning of the stream.
ID3Buffer id3Buffer(mBuffer, mBufferLength);
nsresult rv = id3Buffer.Parse();
if (rv == NS_OK) {
mOffset += id3Buffer.GetMP3Offset()-(mBufferLength-copyLength);
mBufferLength = 0;
}
}
if (mBufferLength >= MP3Buffer::MP3_HEADER_LENGTH) {
// Or there could be a regular frame header somewhere
// in the stream.
MP3Buffer mp3Buffer(mBuffer, mBufferLength);
nsresult rv = mp3Buffer.Parse();
if (rv == NS_OK) {
mDurationUs += mp3Buffer.GetDuration();
mBitRateSum += mp3Buffer.GetBitRateSum();
mNumFrames += mp3Buffer.GetNumberOfFrames();
mOffset += mp3Buffer.GetFrameSizeSum()-(mBufferLength-copyLength);
mBufferLength = 0;
}
}
if (mBufferLength) {
// We have not been able to successfully parse the
// content of the temporary buffer. If the buffer is
// full already, the stream does not contain MP3.
mOffset += copyLength;
mIsMP3 = (mBufferLength < NS_ARRAY_LENGTH(mBuffer));
} else {
// We parsed the temporary buffer. The parser code
// will update the input data.
copyLength = 0;
}
if (mOffset > mLength) {
mLength = mOffset;
}
return copyLength;
}
void MP3FrameParser::Parse(const uint8_t* aBuffer, uint32_t aLength, int64_t aOffset)
{
MutexAutoLock mon(mLock);
// We first try to parse the remaining data from the last call that
// is stored in an internal buffer.
size_t bufferIncr = ParseInternalBuffer(aBuffer, aLength, aOffset);
aBuffer += bufferIncr;
aLength -= bufferIncr;
aOffset += bufferIncr;
// The number of attempts to parse the data. This should be 1 of we
// append to the end of the existing data.
int retries = 1;
if (aOffset+aLength <= mOffset) {
// We already processed this fragment.
return;
} else if (aOffset < mOffset) {
// mOffset is within the new fragment, shorten range.
aLength -= mOffset-aOffset;
aBuffer += mOffset-aOffset;
aOffset = mOffset;
} else if (aOffset > mOffset) {
// Fragment comes after current position, store difference.
mUnhandled += aOffset-mOffset;
// We might start in the middle of a frame and have find the next
// frame header. As our detection heuristics might return false
// positives, we simply try multiple times. The current value comes
// from experimentation with MP3 files. If you encounter false positives
// and incorrectly parsed MP3 files, try incrementing this value.
retries = 5;
}
uint32_t trailing = 0;
while (retries) {
MP3Buffer mp3Buffer(aBuffer, aLength);
nsresult rv = mp3Buffer.Parse();
if (rv != NS_OK) {
--retries;
if (!retries) {
mIsMP3 = false;
return;
}
// We might be in the middle of a frame, find next frame header
const uint8_t *buffer = MP3Buffer::FindNextHeader(aBuffer+1, aLength-1);
mUnhandled += buffer-aBuffer;
mOffset = aOffset + buffer-aBuffer;
aLength -= buffer-aBuffer;
aBuffer = buffer;
} else {
mDurationUs += mp3Buffer.GetDuration();
mBitRateSum += mp3Buffer.GetBitRateSum();
mNumFrames += mp3Buffer.GetNumberOfFrames();
mOffset += mp3Buffer.GetFrameSizeSum();
trailing = mp3Buffer.GetTrailing();
retries = 0;
}
}
if (trailing) {
// Store trailing bytes in temporary buffer.
MOZ_ASSERT(trailing < (NS_ARRAY_LENGTH(mBuffer)*sizeof(*mBuffer)));
memcpy(mBuffer, aBuffer+(aLength-trailing), trailing);
mBufferLength = trailing;
}
if (mOffset > mLength) {
mLength = mOffset;
}
}
void MP3FrameParser::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
{
Parse(reinterpret_cast<const uint8_t*>(aBuffer), aLength, aOffset);
}
int64_t MP3FrameParser::GetDuration()
{
MutexAutoLock mon(mLock);
if (!mNumFrames) {
return -1; // Not a single frame decoded yet
}
// Compute the duration of the unhandled fragments from
// the average bitrate.
int64_t avgBitRate = mBitRateSum / mNumFrames;
NS_ENSURE_TRUE(avgBitRate > 0, mDurationUs);
MOZ_ASSERT(mLength >= mOffset);
int64_t unhandled = mUnhandled + (mLength-mOffset);
return mDurationUs + (uint64_t(MP3Buffer::MP3_DURATION_CONST) * unhandled) / avgBitRate;
}
}

View File

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 <stdint.h>
#include "mozilla/Mutex.h"
namespace mozilla {
// A description of the MP3 format and its extensions is available at
//
// http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
//
// The data in MP3 streams is split into small frames, with each frame
// containing a fixed number of samples. The duration of a frame depends
// on the frame's bit rate and sample rate. Both values can vary among
// frames, so it is necessary to examine each individual frame of an MP3
// stream to calculate the stream's overall duration.
//
// The MP3 frame parser extracts information from an MP3 data stream. It
// accepts a range of frames of an MP3 stream as input, and parses all
// frames for their duration. Callers can query the stream's overall
// duration from the parser.
//
// Call the methods NotifyDataArrived or Parse to add new data. If you added
// information for a certain stream position, you cannot go back to previous
// positions. The parser will simply ignore the input. If you skip stream
// positions, the duration of the related MP3 frames will be estimated from
// the stream's average.
//
// The method GetDuration returns calculated duration of the stream, including
// estimates for skipped ranges.
//
// All public methods are thread-safe.
class MP3FrameParser
{
public:
MP3FrameParser(int64_t aLength=-1);
bool IsMP3() {
MutexAutoLock mon(mLock);
return mIsMP3;
}
void Parse(const uint8_t* aBuffer, uint32_t aLength, int64_t aOffset);
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
int64_t GetDuration();
private:
size_t ParseInternalBuffer(const uint8_t* aBuffer, uint32_t aLength, int64_t aOffset);
uint8_t mBuffer[10];
uint32_t mBufferLength;
// A low-contention lock for protecting the parser results
Mutex mLock;
// All fields below are protected by mLock
uint64_t mDurationUs;
uint64_t mBitRateSum;
uint64_t mNumFrames;
int64_t mOffset;
int64_t mUnhandled;
int64_t mLength;
uint32_t mTrailing;
// Contains the state of the MP3 detection
bool mIsMP3;
};
}

View File

@ -270,6 +270,15 @@ bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
return true;
}
void MediaOmxReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
{
android::OmxDecoder *omxDecoder = mOmxDecoder.get();
if (omxDecoder) {
omxDecoder->NotifyDataArrived(aBuffer, aLength, aOffset);
}
}
bool MediaOmxReader::DecodeAudioData()
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");

View File

@ -40,6 +40,8 @@ public:
virtual nsresult Init(MediaDecoderReader* aCloneDonor);
virtual nsresult ResetDecode();
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
virtual bool DecodeAudioData();
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold);

View File

@ -18,6 +18,8 @@
#include "mozilla/Preferences.h"
#include "mozilla/Types.h"
#include "mozilla/Monitor.h"
#include "nsMimeTypes.h"
#include "MPAPI.h"
#include "prlog.h"
@ -37,6 +39,116 @@ using namespace MPAPI;
using namespace mozilla;
namespace mozilla {
class OmxDecoderProcessCachedDataTask : public Task
{
public:
OmxDecoderProcessCachedDataTask(android::OmxDecoder* aOmxDecoder, int64_t aOffset)
: mOmxDecoder(aOmxDecoder),
mOffset(aOffset)
{ }
void Run()
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(mOmxDecoder.get());
mOmxDecoder->ProcessCachedData(mOffset, false);
}
private:
android::sp<android::OmxDecoder> mOmxDecoder;
int64_t mOffset;
};
// When loading an MP3 stream from a file, we need to parse the file's
// content to find its duration. Reading files of 100 Mib or more can
// delay the player app noticably, so the file os read and decoded in
// smaller chunks.
//
// We first read on the decode thread, but parsing must be done on the
// main thread. After we read the file's initial MiBs in the decode
// thread, an instance of this class is scheduled to the main thread for
// parsing the MP3 stream. The decode thread waits until it has finished.
//
// If there is more data available from the file, the runnable dispatches
// a task to the IO thread for retrieving the next chunk of data, and
// the IO task dispatches a runnable to the main thread for parsing the
// data. This goes on until all of the MP3 file has been parsed.
class OmxDecoderNotifyDataArrivedRunnable : public nsRunnable
{
public:
OmxDecoderNotifyDataArrivedRunnable(android::OmxDecoder* aOmxDecoder, const char* aBuffer, uint64_t aLength, int64_t aOffset, uint64_t aFullLength)
: mOmxDecoder(aOmxDecoder),
mBuffer(aBuffer),
mLength(aLength),
mOffset(aOffset),
mFullLength(aFullLength),
mCompletedMonitor("OmxDecoderNotifyDataArrived.mCompleted"),
mCompleted(false)
{
MOZ_ASSERT(mOmxDecoder.get());
MOZ_ASSERT(mBuffer.get() || !mLength);
}
NS_IMETHOD Run()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
const char* buffer = mBuffer.get();
while (mLength) {
uint32_t length = std::min<uint64_t>(mLength, UINT32_MAX);
mOmxDecoder->NotifyDataArrived(mBuffer.get(), mLength, mOffset);
buffer += length;
mLength -= length;
mOffset += length;
}
if (mOffset < mFullLength) {
// We cannot read data in the main thread because it
// might block for too long. Instead we post an IO task
// to the IO thread if there is more data available.
XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new OmxDecoderProcessCachedDataTask(mOmxDecoder.get(), mOffset));
}
Completed();
return NS_OK;
}
void WaitForCompletion()
{
MOZ_ASSERT(!NS_IsMainThread());
MonitorAutoLock mon(mCompletedMonitor);
if (!mCompleted) {
mCompletedMonitor.Wait();
}
}
private:
// Call this function at the end of Run() to notify waiting
// threads.
void Completed()
{
MonitorAutoLock mon(mCompletedMonitor);
MOZ_ASSERT(!mCompleted);
mCompleted = true;
mCompletedMonitor.Notify();
}
android::sp<android::OmxDecoder> mOmxDecoder;
nsAutoArrayPtr<const char> mBuffer;
uint64_t mLength;
int64_t mOffset;
uint64_t mFullLength;
Monitor mCompletedMonitor;
bool mCompleted;
};
namespace layers {
VideoGraphicBuffer::VideoGraphicBuffer(const android::wp<android::OmxDecoder> aOmxDecoder,
@ -147,6 +259,7 @@ OmxDecoder::OmxDecoder(MediaResource *aResource,
mAudioChannels(-1),
mAudioSampleRate(-1),
mDurationUs(-1),
mMP3FrameParser(aResource->GetLength()),
mVideoBuffer(nullptr),
mAudioBuffer(nullptr),
mIsVideoSeeking(false),
@ -274,9 +387,27 @@ bool OmxDecoder::TryLoad() {
if (durationUs > totalDurationUs)
totalDurationUs = durationUs;
}
if (mAudioTrack.get() && mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > totalDurationUs)
totalDurationUs = durationUs;
if (mAudioTrack.get()) {
durationUs = -1;
const char* audioMime;
sp<MetaData> meta = mAudioTrack->getFormat();
if (meta->findCString(kKeyMIMEType, &audioMime) && !strcasecmp(audioMime, AUDIO_MP3)) {
// Feed MP3 parser with cached data. Local files will be fully
// cached already, network streams will update with sucessive
// calls to NotifyDataArrived.
if (ProcessCachedData(0, true)) {
durationUs = mMP3FrameParser.GetDuration();
if (durationUs > totalDurationUs) {
totalDurationUs = durationUs;
}
}
}
if ((durationUs == -1) && meta->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > totalDurationUs) {
totalDurationUs = durationUs;
}
}
}
mDurationUs = totalDurationUs;
@ -485,6 +616,25 @@ bool OmxDecoder::SetAudioFormat() {
return true;
}
void OmxDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
{
if (!mMP3FrameParser.IsMP3()) {
return;
}
mMP3FrameParser.NotifyDataArrived(aBuffer, aLength, aOffset);
int64_t durationUs = mMP3FrameParser.GetDuration();
if (durationUs != mDurationUs) {
mDurationUs = durationUs;
MOZ_ASSERT(mDecoder);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->UpdateMediaDuration(mDurationUs);
}
}
void OmxDecoder::ReleaseVideoBuffer() {
if (mVideoBuffer) {
mVideoBuffer->release();
@ -842,3 +992,43 @@ void OmxDecoder::ReleaseAllPendingVideoBuffersLocked()
releasingVideoBuffers.clear();
}
bool OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion)
{
// We read data in chunks of 8 MiB. We can reduce this
// value if media, such as sdcards, is too slow.
static const int64_t sReadSize = 8 * 1024 * 1024;
NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
MOZ_ASSERT(mResource);
int64_t resourceLength = mResource->GetCachedDataEnd(0);
NS_ENSURE_TRUE(resourceLength >= 0, false);
if (aOffset >= resourceLength) {
return true; // Cache is empty, nothing to do
}
int64_t bufferLength = std::min<int64_t>(resourceLength-aOffset, sReadSize);
nsAutoArrayPtr<char> buffer(new char[bufferLength]);
nsresult rv = mResource->ReadFromCache(buffer.get(), aOffset, bufferLength);
NS_ENSURE_SUCCESS(rv, false);
nsRefPtr<OmxDecoderNotifyDataArrivedRunnable> runnable(
new OmxDecoderNotifyDataArrivedRunnable(this,
buffer.forget(),
bufferLength,
aOffset,
resourceLength));
rv = NS_DispatchToMainThread(runnable.get());
NS_ENSURE_SUCCESS(rv, false);
if (aWaitForCompletion) {
runnable->WaitForCompletion();
}
return true;
}

View File

@ -9,6 +9,7 @@
#include "GonkNativeWindow.h"
#include "GonkNativeWindowClient.h"
#include "GrallocImages.h"
#include "MP3FrameParser.h"
#include "MPAPI.h"
#include "MediaResource.h"
#include "AbstractMediaDecoder.h"
@ -76,6 +77,7 @@ private:
class OmxDecoder : public OMXCodecProxy::EventListener {
typedef MPAPI::AudioFrame AudioFrame;
typedef MPAPI::VideoFrame VideoFrame;
typedef mozilla::MP3FrameParser MP3FrameParser;
typedef mozilla::MediaResource MediaResource;
typedef mozilla::AbstractMediaDecoder AbstractMediaDecoder;
@ -109,6 +111,7 @@ class OmxDecoder : public OMXCodecProxy::EventListener {
int64_t mDurationUs;
VideoFrame mVideoFrame;
AudioFrame mAudioFrame;
MP3FrameParser mMP3FrameParser;
// Lifetime of these should be handled by OMXCodec, as long as we release
// them after use: see ReleaseVideoBuffer(), ReleaseAudioBuffer()
@ -177,6 +180,8 @@ public:
bool SetVideoFormat();
bool SetAudioFormat();
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
void GetDuration(int64_t *durationUs) {
*durationUs = mDurationUs;
}
@ -199,7 +204,7 @@ public:
return mAudioSource != nullptr;
}
bool ReadVideo(VideoFrame *aFrame, int64_t aSeekTimeUs,
bool ReadVideo(VideoFrame *aFrame, int64_t aSeekTimeUs,
bool aKeyframeSkip = false,
bool aDoSeek = false);
bool ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs);
@ -220,6 +225,7 @@ public:
// Called on ALooper thread.
void onMessageReceived(const sp<AMessage> &msg);
bool ProcessCachedData(int64_t aOffset, bool aWaitForCompletion);
};
}

View File

@ -14,6 +14,7 @@ EXPORTS += [
CPP_SOURCES += [
'MediaOmxDecoder.cpp',
'MediaOmxReader.cpp',
'MP3FrameParser.cpp',
'OmxDecoder.cpp',
'OMXCodecProxy.cpp',
]

View File

@ -116,6 +116,8 @@ MOCHITEST_FILES = \
test_video_to_canvas.html \
test_audiowrite.html \
test_mediarecorder_creation.html \
test_mediarecorder_record_audiocontext.html \
test_mediarecorder_record_stopms.html \
test_mozHasAudio.html \
test_source_media.html \
test_autoplay_contentEditable.html \
@ -258,6 +260,7 @@ MOCHITEST_FILES += \
test-6-5.1.opus \
test-7-6.1.opus \
test-8-7.1.opus \
vbr.mp3 \
video-overhang.ogg \
file_a4_tone.ogg \
detodos.opus \

View File

@ -42,6 +42,7 @@ var gPlayedTests = [
{ name:"seek.webm", type:"video/webm", duration:3.966 },
{ name:"gizmo.mp4", type:"video/mp4", duration:5.56 },
{ name:"owl.mp3", type:"audio/mpeg", duration:3.29 },
{ name:"vbr.mp3", type:"audio/mpeg", duration:10.0 }
];
// Used by test_mozLoadFrom. Need one test file per decoder backend, plus
@ -154,10 +155,10 @@ var gPlayTests = [
// Test playback of a WebM file with non-zero start time.
{ name:"split.webm", type:"video/webm", duration:1.967 },
// Test playback of a raw file
{ name:"seek.yuv", type:"video/x-raw-yuv", duration:1.833 },
// A really short, low sample rate, single channel file. This tests whether
// we can handle playing files when only push very little audio data to the
// hardware.
@ -563,7 +564,7 @@ const DEBUG_TEST_LOOP_FOREVER = false;
// 1. Create a new MediaTestManager object.
// 2. Create a test startTest function. This takes a test object and a token,
// and performs anything necessary to start the test. The test object is an
// element in one of the g*Tests above. Your startTest function must call
// element in one of the g*Tests above. Your startTest function must call
// MediaTestManager.start(token) if it starts a test. The test object is
// guaranteed to be playable by our supported decoders; you don't need to
// check canPlayType.
@ -574,12 +575,12 @@ function MediaTestManager() {
// Sets up a MediaTestManager to runs through the 'tests' array, which needs
// to be one of, or have the same fields as, the g*Test arrays of tests. Uses
// the user supplied 'startTest' function to initialize the test. This
// the user supplied 'startTest' function to initialize the test. This
// function must accept two arguments, the test entry from the 'tests' array,
// and a token. Call MediaTestManager.started(token) if you start the test,
// and MediaTestManager.finished(token) when the test finishes. You don't have
// to start every test, but if you call started() you *must* call finish()
// else you'll timeout.
// else you'll timeout.
this.runTests = function(tests, startTest) {
this.startTime = new Date();
SimpleTest.info("Started " + this.startTime + " (" + this.startTime.getTime()/1000 + "s)");
@ -593,7 +594,7 @@ function MediaTestManager() {
SimpleTest.waitForExplicitFinish();
this.nextTest();
}
// Registers that the test corresponding to 'token' has been started.
// Don't call more than once per token.
this.started = function(token) {
@ -601,7 +602,7 @@ function MediaTestManager() {
this.numTestsRunning++;
is(this.numTestsRunning, this.tokens.length, "[started " + token + "] Length of array should match number of running tests");
}
// Registers that the test corresponding to 'token' has finished. Call when
// you've finished your test. If all tests are complete this will finish the
// run, otherwise it may start up the next run. It's ok to call multiple times
@ -626,7 +627,7 @@ function MediaTestManager() {
// with live threads waiting for the GC are killed promptly, to free up the
// thread stacks' address space.
SpecialPowers.forceGC();
while (this.testNum < this.tests.length && this.tokens.length < PARALLEL_TESTS) {
var test = this.tests[this.testNum];
var token = (test.name ? (test.name + "-"): "") + this.testNum;
@ -635,11 +636,11 @@ function MediaTestManager() {
if (DEBUG_TEST_LOOP_FOREVER && this.testNum == this.tests.length) {
this.testNum = 0;
}
// Ensure we can play the resource type.
if (test.type && !document.createElement('video').canPlayType(test.type))
continue;
// Do the init. This should start the test.
this.startTest(test, token);
}

View File

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test MediaRecorder Record AudioContext</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<script class="testbody" type="text/javascript">
function startTest() {
var context = new AudioContext();
var hasonstop = false;
var buffer = context.createBuffer(1, 80920, context.sampleRate);
for (var i = 0; i < 80920; ++i) {
buffer.getChannelData(0)[i] = Math.sin(1000 * 2 * Math.PI * i / context.sampleRate);
}
var source = context.createBufferSource();
source.buffer = buffer;
var dest = context.createMediaStreamDestination();
source.connect(dest);
var elem = document.createElement('audio');
elem.mozSrcObject = dest.stream;
mMediaStream = dest.stream;
source.start(0);
elem.play();
mMediaRecorder = new MediaRecorder(dest.stream);
mMediaRecorder.onwarning = function() {
ok(false, 'onwarning unexpectedly fired');
};
mMediaRecorder.onerror = function() {
ok(false, 'onerror unexpectedly fired');
};
mMediaRecorder.onstop = function() {
ok(true, 'onstop fired successfully');
is(mMediaRecorder.state, 'inactive', 'check recording status is inactive');
SimpleTest.finish();
};
mMediaRecorder.ondataavailable = function (e) {
if (mMediaRecorder.state == 'recording') {
is('audio/ogg', mMediaRecorder.mimeType, "check the record mimetype return " + mMediaRecorder.mimeType);
ok(e.data.size > 0, 'check blob has data');
mMediaRecorder.stop();
}
};
try {
mMediaRecorder.start(1000);
is('recording', mMediaRecorder.state, "check record state recording");
} catch (e) {
ok(false, 'Can t record audio context');
}
}
startTest();
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test MediaRecorder Record Stopped Stream</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<div id="content" style="display: none">
<audio id="testAudio"></audio>
</div>
<script class="testbody" type="text/javascript">
function startTest() {
navigator.mozGetUserMedia({audio: true, fake: true}, function(stream) {
var testAudio = document.getElementById('testAudio');
testAudio.onended = function() {
var mediaRecorder = new MediaRecorder(stream);
try {
mediaRecorder.start();
ok(false, 'Recording a stopped stream failed to throw an exception');
} catch (e) {
is(e.name, 'InvalidStateError',
'Recording a stopped stream threw an InvalidStateError');
} finally {
SimpleTest.finish();
}
};
testAudio.mozSrcObject = stream;
testAudio.play();
stream.stop();
}, function(err) {
ok(false, 'Unexpected error fired with: ' + err);
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
startTest();
</script>
</pre>
</body>
</html>

BIN
content/media/test/vbr.mp3 Normal file

Binary file not shown.

View File

@ -97,6 +97,8 @@ public:
virtual void SetMediaDuration(int64_t aDuration) MOZ_FINAL MOZ_OVERRIDE;
virtual void UpdateMediaDuration(int64_t aDuration) MOZ_FINAL MOZ_OVERRIDE;
virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;
virtual void SetTransportSeekable(bool aTransportSeekable) MOZ_FINAL MOZ_OVERRIDE;
@ -214,6 +216,12 @@ BufferDecoder::SetMediaDuration(int64_t aDuration)
// ignore
}
void
BufferDecoder::UpdateMediaDuration(int64_t aDuration)
{
// ignore
}
void
BufferDecoder::SetMediaSeekable(bool aMediaSeekable)
{

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaStreamAudioDestinationNode.h"
#include "nsIDocument.h"
#include "mozilla/dom/AudioStreamTrack.h"
#include "mozilla/dom/MediaStreamAudioDestinationNodeBinding.h"
#include "AudioNodeEngine.h"
@ -73,6 +74,11 @@ MediaStreamAudioDestinationNode::MediaStreamAudioDestinationNode(AudioContext* a
MediaStreamDestinationEngine* engine = new MediaStreamDestinationEngine(this, tus);
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
mPort = tus->AllocateInputPort(mStream, 0);
nsIDocument* doc = aContext->GetParentObject()->GetExtantDoc();
if (doc) {
mDOMStream->CombineWithPrincipal(doc->NodePrincipal());
}
}
void

View File

@ -17,6 +17,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageBroadcaster");
XPCOMUtils.defineLazyServiceGetter(this, "NetUtil",
"@mozilla.org/network/util;1",
"nsINetUtil");
this.EXPORTED_SYMBOLS = [];
let idbGlobal = this;
@ -158,6 +162,7 @@ let Activities = {
"Activities:Register",
"Activities:Unregister",
"Activities:GetContentTypes"
],
init: function activities_init() {
@ -311,9 +316,18 @@ let Activities = {
break;
case "Activities:Register":
let self = this;
this.db.add(msg,
function onSuccess(aEvent) {
mm.sendAsyncMessage("Activities:Register:OK", null);
let res = [];
msg.forEach(function(aActivity) {
self.updateContentTypeList(aActivity, res);
});
if (res.length) {
ppmm.broadcastAsyncMessage("Activities:RegisterContentTypes",
{ contentTypes: res });
}
},
function onError(aEvent) {
msg.error = "REGISTER_ERROR";
@ -322,8 +336,60 @@ let Activities = {
break;
case "Activities:Unregister":
this.db.remove(msg);
let res = [];
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;
}
},
updateContentTypeList: function updateContentTypeList(aActivity, aResult) {
// Bail out if this is not a "view" activity.
if (aActivity.name != "view") {
return;
}
let types = aActivity.description.filters.type;
if (typeof types == "string") {
types = [types];
}
// Check that this is a real content type and sanitize it.
types.forEach(function(aContentType) {
let hadCharset = { };
let charset = { };
let contentType =
NetUtil.parseContentType(aContentType, charset, hadCharset);
if (contentType) {
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

@ -688,7 +688,8 @@ this.DOMApplicationRegistry = {
for (let activity in root.activities) {
let description = root.activities[activity];
activitiesToUnregister.push({ "manifest": aApp.manifestURL,
"name": activity });
"name": activity,
"description": description });
}
return activitiesToUnregister;
},

View File

@ -40,7 +40,7 @@ this.DOMRequestIpcHelperMessageListener = function(aHelper, aWindow, aMessages)
this._messages = aMessages;
this._messages.forEach(function(msgName) {
cpmm.addMessageListener(msgName, this);
cpmm.addWeakMessageListener(msgName, this);
}, this);
Services.obs.addObserver(this, "inner-window-destroyed", /* weakRef */ true);
@ -91,7 +91,7 @@ DOMRequestIpcHelperMessageListener.prototype = {
Services.obs.removeObserver(this, "inner-window-destroyed");
this._messages.forEach(function(msgName) {
cpmm.removeMessageListener(msgName, this);
cpmm.removeWeakMessageListener(msgName, this);
}, this);
this._messages = null;
@ -117,7 +117,7 @@ DOMRequestIpcHelper.prototype = {
new DOMRequestIpcHelperMessageListener(this, aWindow, aMessages);
this._window = aWindow;
this._requests = [];
this._requests = {};
this._id = this._getRandomId();
if (this._window) {
@ -166,7 +166,7 @@ DOMRequestIpcHelper.prototype = {
this._destroyed = true;
this._DOMRequestIpcHelperMessageListener.destroy();
this._requests = [];
this._requests = {};
this._window = null;
if(this.uninit) {

View File

@ -1140,7 +1140,7 @@ Navigator::GetMozCellBroadcast(ErrorResult& aRv)
return mCellBroadcast;
}
nsIDOMTelephony*
telephony::Telephony*
Navigator::GetMozTelephony(ErrorResult& aRv)
{
if (!mTelephony) {

View File

@ -33,7 +33,6 @@ class systemMessageCallback;
#endif
#ifdef MOZ_B2G_RIL
class nsIDOMTelephony;
class nsIDOMMozMobileConnection;
class nsIDOMMozCellBroadcast;
class nsIDOMMozVoicemail;
@ -86,6 +85,12 @@ class MobileConnection;
#endif
} // namespace Connection;
#ifdef MOZ_B2G_RIL
namespace telephony {
class Telephony;
} // namespace Telephony;
#endif
namespace power {
class PowerManager;
} // namespace power
@ -214,7 +219,7 @@ public:
ErrorResult& aRv);
bool MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv);
#ifdef MOZ_B2G_RIL
nsIDOMTelephony* GetMozTelephony(ErrorResult& aRv);
telephony::Telephony* GetMozTelephony(ErrorResult& aRv);
nsIDOMMozMobileConnection* GetMozMobileConnection(ErrorResult& aRv);
nsIDOMMozCellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
nsIDOMMozVoicemail* GetMozVoicemail(ErrorResult& aRv);
@ -309,7 +314,7 @@ private:
nsRefPtr<power::PowerManager> mPowerManager;
nsRefPtr<MobileMessageManager> mMobileMessageManager;
#ifdef MOZ_B2G_RIL
nsCOMPtr<nsIDOMTelephony> mTelephony;
nsRefPtr<telephony::Telephony> mTelephony;
nsCOMPtr<nsIDOMMozVoicemail> mVoicemail;
#endif
nsRefPtr<network::Connection> mConnection;

View File

@ -225,8 +225,6 @@ using mozilla::dom::workers::ResolveWorkerClasses;
#include "nsIDOMConnection.h"
#ifdef MOZ_B2G_RIL
#include "Telephony.h"
#include "TelephonyCall.h"
#include "nsIDOMMozVoicemail.h"
#include "nsIDOMIccManager.h"
#include "nsIDOMMozCellBroadcast.h"
@ -640,10 +638,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
DOM_DEFAULT_SCRIPTABLE_FLAGS)
#ifdef MOZ_B2G_RIL
NS_DEFINE_CLASSINFO_DATA(Telephony, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(TelephonyCall, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozVoicemail, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozIccManager, nsDOMGenericSH,
@ -1529,15 +1523,6 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_END
#ifdef MOZ_B2G_RIL
DOM_CLASSINFO_MAP_BEGIN(Telephony, nsIDOMTelephony)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMTelephony)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(TelephonyCall, nsIDOMTelephonyCall)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMTelephonyCall)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(MozVoicemail, nsIDOMMozVoicemail)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozVoicemail)

View File

@ -128,8 +128,6 @@ DOMCI_CLASS(CSSPageRule)
DOMCI_CLASS(MediaQueryList)
#ifdef MOZ_B2G_RIL
DOMCI_CLASS(Telephony)
DOMCI_CLASS(TelephonyCall)
DOMCI_CLASS(MozVoicemail)
DOMCI_CLASS(MozIccManager)
#endif

View File

@ -150,6 +150,16 @@ DOMInterfaces = {
'headerFile': 'BatteryManager.h'
},
'CallEvent': {
'nativeType': 'mozilla::dom::telephony::CallEvent',
'headerFile': 'CallEvent.h',
},
'CallsList': {
'nativeType': 'mozilla::dom::telephony::CallsList',
'headerFile': 'CallsList.h',
},
'CameraManager': {
'nativeType': 'nsDOMCameraManager',
'headerFile': 'DOMCameraManager.h'
@ -1118,6 +1128,16 @@ DOMInterfaces = {
'concrete': False,
},
'Telephony' : {
'nativeType': 'mozilla::dom::telephony::Telephony',
'headerFile': 'Telephony.h',
},
'TelephonyCall' : {
'nativeType': 'mozilla::dom::telephony::TelephonyCall',
'headerFile': 'TelephonyCall.h',
},
'Text': {
# Total hack to allow binding code to realize that nsTextNode can
# in fact be cast to Text.

View File

@ -9,7 +9,9 @@
#include "BluetoothService.h"
#include "BluetoothCommon.h"
#include "BluetoothHfpManager.h"
#include "BluetoothManager.h"
#include "BluetoothOppManager.h"
#include "BluetoothParent.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothServiceChildProcess.h"
@ -475,6 +477,14 @@ BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
NS_ENSURE_SUCCESS(rv, rv);
}
if (!aStart) {
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
hfp->Disconnect();
BluetoothOppManager* opp = BluetoothOppManager::Get();
opp->Disconnect();
}
nsCOMPtr<nsIRunnable> runnable = new ToggleBtTask(aStart, aIsStartup);
rv = mBluetoothCommandThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -92,6 +92,12 @@ TelephonyListener::NotifyError(int32_t aCallIndex,
return NS_OK;
}
NS_IMETHODIMP
TelephonyListener::NotifyCdmaCallWaiting(const nsAString& aNumber)
{
return NS_OK;
}
} // anonymous namespace
BluetoothTelephonyListener::BluetoothTelephonyListener()

View File

@ -34,14 +34,16 @@
#include "nsDebug.h"
#include "nsDataHashtable.h"
#include "mozilla/Atomics.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/Hal.h"
#include "mozilla/ipc/UnixSocket.h"
#include "mozilla/ipc/DBusThread.h"
#include "mozilla/ipc/DBusUtils.h"
#include "mozilla/ipc/RawDBusConnection.h"
#include "mozilla/Util.h"
#include "mozilla/Mutex.h"
#include "mozilla/NullPtr.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/Util.h"
#if defined(MOZ_WIDGET_GONK)
#include "cutils/properties.h"
#endif
@ -75,6 +77,13 @@ USING_BLUETOOTH_NAMESPACE
#define ERR_AVRCP_IS_DISCONNECTED "AvrcpIsDisconnected"
#define ERR_UNKNOWN_PROFILE "UnknownProfileError"
/**
* To not lock Bluetooth switch button on Settings UI because of any accident,
* we will force disabling Bluetooth 5 seconds after the user requesting to
* turn off Bluetooth.
*/
#define TIMEOUT_FORCE_TO_DISABLE_BT 5
typedef struct {
const char* name;
int type;
@ -159,8 +168,10 @@ static const char* sBluetoothDBusSignals[] =
static nsRefPtr<RawDBusConnection> gThreadConnection;
static nsDataHashtable<nsStringHashKey, DBusMessage* > sPairingReqTable;
static nsDataHashtable<nsStringHashKey, DBusMessage* > sAuthorizeReqTable;
static Atomic<int32_t> sIsPairing;
static nsString sAdapterPath;
static Atomic<int32_t> sIsPairing(0);
static int sConnectedDeviceCount = 0;
static Monitor sStopBluetoothMonitor("BluetoothService.sStopBluetoothMonitor");
typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&);
typedef bool (*FilterFunc)(const BluetoothValue&);
@ -1499,6 +1510,19 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
signal.path() = NS_LITERAL_STRING(KEY_ADAPTER);
signal.value() = parameters;
NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal));
} else if (property.name().EqualsLiteral("Connected")) {
MonitorAutoLock lock(sStopBluetoothMonitor);
if (property.value().get_bool()) {
++sConnectedDeviceCount;
} else {
MOZ_ASSERT(sConnectedDeviceCount > 0);
--sConnectedDeviceCount;
if (sConnectedDeviceCount == 0) {
lock.Notify();
}
}
}
} else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "AdapterAdded")) {
const char* str;
@ -1689,11 +1713,11 @@ BluetoothDBusService::StopInternal()
// This could block. It should never be run on the main thread.
MOZ_ASSERT(!NS_IsMainThread());
// If Bluetooth is turned off while connections exist, in order not to only
// disconnect with profile connections with low level ACL connections alive,
// we disconnect ACLs directly instead of closing each socket.
if (!sAdapterPath.IsEmpty()) {
DisconnectAllAcls(sAdapterPath);
{
MonitorAutoLock lock(sStopBluetoothMonitor);
if (sConnectedDeviceCount > 0) {
lock.Wait(PR_SecondsToInterval(TIMEOUT_FORCE_TO_DISABLE_BT));
}
}
if (!mConnection) {
@ -1737,6 +1761,7 @@ BluetoothDBusService::StopInternal()
sAuthorizeReqTable.Clear();
sIsPairing = 0;
sConnectedDeviceCount = 0;
StopDBus();
return NS_OK;

View File

@ -22,7 +22,7 @@ var gData = [
perm: ["telephony"],
needParentPerm: true,
obj: "mozTelephony",
idl: "nsIDOMTelephony",
webidl: "Telephony",
},
]
</script>

View File

@ -105,7 +105,8 @@ const RIL_IPC_MSG_NAMES = [
"RIL:ReadIccContacts",
"RIL:UpdateIccContact",
"RIL:SetRoamingPreference",
"RIL:GetRoamingPreference"
"RIL:GetRoamingPreference",
"RIL:CdmaCallWaiting"
];
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
@ -1600,6 +1601,11 @@ RILContentHelper.prototype = {
this.handleSimpleRequest(msg.json.requestId, msg.json.errorMsg,
msg.json.mode);
break;
case "RIL:CdmaCallWaiting":
this._deliverEvent("_telephonyListeners",
"notifyCdmaCallWaiting",
[msg.json.data]);
break;
}
},

View File

@ -943,6 +943,10 @@ RadioInterface.prototype = {
// This one will handle its own notifications.
this.handleCallDisconnected(message.call);
break;
case "cdmaCallWaiting":
gMessageManager.sendTelephonyMessage("RIL:CdmaCallWaiting",
this.clientId, message.number);
break;
case "enumerateCalls":
// This one will handle its own notifications.
this.handleEnumerateCalls(message);
@ -1829,7 +1833,8 @@ RadioInterface.prototype = {
*/
handleSuppSvcNotification: function handleSuppSvcNotification(message) {
message.notification = convertRILSuppSvcNotification(message.notification);
this._sendTelephonyMessage("RIL:SuppSvcNotification", message);
gMessageManager.sendTelephonyMessage("RIL:SuppSvcNotification",
this.clientId, message);
},
/**

View File

@ -1955,7 +1955,13 @@ let RIL = {
holdCall: function holdCall(options) {
let call = this.currentCalls[options.callIndex];
if (call && call.state == CALL_STATE_ACTIVE) {
Buf.simpleRequest(REQUEST_SWITCH_HOLDING_AND_ACTIVE);
if (this._isCdma) {
Buf.newParcel(REQUEST_CDMA_FLASH);
Buf.writeString("");
Buf.sendParcel();
} else {
Buf.simpleRequest(REQUEST_SWITCH_HOLDING_AND_ACTIVE);
}
}
},
@ -6179,7 +6185,19 @@ RIL[UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS] = function UNSOLICITED_RESPONSE_NEW_
RIL[UNSOLICITED_CDMA_RUIM_SMS_STORAGE_FULL] = null;
RIL[UNSOLICITED_RESTRICTED_STATE_CHANGED] = null;
RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE] = null;
RIL[UNSOLICITED_CDMA_CALL_WAITING] = null;
RIL[UNSOLICITED_CDMA_CALL_WAITING] = function UNSOLICITED_CDMA_CALL_WAITING(length) {
let call = {};
call.number = Buf.readString();
call.numberPresentation = Buf.readUint32();
call.name = Buf.readString();
call.namePresentation = Buf.readUint32();
call.isPresent = Buf.readUint32();
call.signalType = Buf.readUint32();
call.alertPitch = Buf.readUint32();
call.signal = Buf.readUint32();
this.sendChromeMessage({rilMessageType: "cdmaCallWaiting",
number: call.number});
};
RIL[UNSOLICITED_CDMA_OTA_PROVISION_STATUS] = null;
RIL[UNSOLICITED_CDMA_INFO_REC] = null;
RIL[UNSOLICITED_OEM_HOOK_RAW] = null;

View File

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CallEvent.h"
#include "mozilla/dom/CallEventBinding.h"
#include "TelephonyCall.h"
USING_TELEPHONY_NAMESPACE
using namespace mozilla::dom;
/* static */
already_AddRefed<CallEvent>
CallEvent::Create(EventTarget* aOwner, const nsAString& aType,
TelephonyCall* aCall, bool aCanBubble,
bool aCancelable)
{
nsRefPtr<CallEvent> event = new CallEvent(aOwner, nullptr, nullptr);
event->mCall = aCall;
event->InitEvent(aType, aCanBubble, aCancelable);
return event.forget();
}
JSObject*
CallEvent::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return CallEventBinding::Wrap(aCx, aScope, this);
}
NS_IMPL_CYCLE_COLLECTION_CLASS(CallEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CallEvent, nsDOMEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCall)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CallEvent, nsDOMEvent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCall)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(CallEvent, nsDOMEvent)
NS_IMPL_RELEASE_INHERITED(CallEvent, nsDOMEvent)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CallEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
// WebIDL
/* static */
already_AddRefed<CallEvent>
CallEvent::Constructor(const GlobalObject& aGlobal, const nsAString& aType,
const CallEventInit& aOptions, ErrorResult& aRv)
{
nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.Get());
if (!target) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsRefPtr<CallEvent> event = Create(target, aType, aOptions.mCall, false, false);
return event.forget();
}
already_AddRefed<TelephonyCall>
CallEvent::GetCall() const
{
nsRefPtr<TelephonyCall> call = mCall;
return call.forget();
}

61
dom/telephony/CallEvent.h Normal file
View File

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_telephony_callevent_h
#define mozilla_dom_telephony_callevent_h
#include "TelephonyCommon.h"
#include "nsDOMEvent.h"
namespace mozilla {
namespace dom {
struct CallEventInit;
}
}
BEGIN_TELEPHONY_NAMESPACE
class CallEvent MOZ_FINAL : public nsDOMEvent
{
nsRefPtr<TelephonyCall> mCall;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CallEvent, nsDOMEvent)
NS_FORWARD_TO_NSDOMEVENT
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
// WebIDL
static already_AddRefed<CallEvent>
Constructor(const GlobalObject& aGlobal, const nsAString& aType,
const CallEventInit& aOptions, ErrorResult& aRv);
already_AddRefed<TelephonyCall>
GetCall() const;
static already_AddRefed<CallEvent>
Create(EventTarget* aOwner, const nsAString& aType, TelephonyCall* aCall,
bool aCanBubble, bool aCancelable);
private:
CallEvent(EventTarget* aOwner,
nsPresContext* aPresContext,
nsEvent* aEvent)
: nsDOMEvent(aOwner, aPresContext, aEvent)
{
SetIsDOMBinding();
}
virtual ~CallEvent()
{ }
};
END_TELEPHONY_NAMESPACE
#endif // mozilla_dom_telephony_callevent_h

View File

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CallsList.h"
#include "mozilla/dom/CallsListBinding.h"
#include "Telephony.h"
#include "TelephonyCall.h"
USING_TELEPHONY_NAMESPACE
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(CallsList, mTelephony)
NS_IMPL_CYCLE_COLLECTING_ADDREF(CallsList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CallsList)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CallsList)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
CallsList::CallsList(Telephony* aTelephony)
: mTelephony(aTelephony)
{
MOZ_ASSERT(mTelephony);
SetIsDOMBinding();
}
CallsList::~CallsList()
{
}
nsPIDOMWindow*
CallsList::GetParentObject() const
{
return mTelephony->GetOwner();
}
JSObject*
CallsList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return CallsListBinding::Wrap(aCx, aScope, this);
}
already_AddRefed<TelephonyCall>
CallsList::Item(uint32_t aIndex) const
{
nsRefPtr<TelephonyCall> call = mTelephony->CallsArray().SafeElementAt(aIndex);
return call.forget();
}
uint32_t
CallsList::Length() const
{
return mTelephony->CallsArray().Length();
}
already_AddRefed<TelephonyCall>
CallsList::IndexedGetter(uint32_t aIndex, bool& aFound) const
{
nsRefPtr<TelephonyCall> call = mTelephony->CallsArray().SafeElementAt(aIndex);
aFound = call ? true : false;
return call.forget();
}

50
dom/telephony/CallsList.h Normal file
View File

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_telephony_CallsList_h__
#define mozilla_dom_telephony_CallsList_h__
#include "TelephonyCommon.h"
#include "nsWrapperCache.h"
BEGIN_TELEPHONY_NAMESPACE
class CallsList MOZ_FINAL : public nsISupports,
public nsWrapperCache
{
nsRefPtr<Telephony> mTelephony;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CallsList)
CallsList(Telephony* aTelephony);
nsPIDOMWindow*
GetParentObject() const;
// WrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
// CallsList WebIDL
already_AddRefed<TelephonyCall>
Item(uint32_t aIndex) const;
uint32_t
Length() const;
already_AddRefed<TelephonyCall>
IndexedGetter(uint32_t aIndex, bool& aFound) const;
private:
~CallsList();
};
END_TELEPHONY_NAMESPACE
#endif // mozilla_dom_telephony_CallsList_h__

View File

@ -5,23 +5,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Telephony.h"
#include "mozilla/dom/TelephonyBinding.h"
#include "nsIURI.h"
#include "nsIDOMCallEvent.h"
#include "nsPIDOMWindow.h"
#include "nsIPermissionManager.h"
#include "GeneratedEvents.h"
#include "jsapi.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsCxPusher.h"
#include "nsDOMClassInfo.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsTArrayHelpers.h"
#include "nsThreadUtils.h"
#include "CallEvent.h"
#include "CallsList.h"
#include "TelephonyCall.h"
#define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1"
@ -78,14 +77,15 @@ public:
};
Telephony::Telephony()
: mActiveCall(nullptr), mCallsArray(nullptr), mRooted(false),
mEnumerated(false)
: mActiveCall(nullptr), mEnumerated(false)
{
if (!gTelephonyList) {
gTelephonyList = new TelephonyList();
}
gTelephonyList->AppendElement(this);
SetIsDOMBinding();
}
Telephony::~Telephony()
@ -98,11 +98,6 @@ Telephony::~Telephony()
}
}
if (mRooted) {
mCallsArray = nullptr;
NS_DROP_JS_OBJECTS(this, Telephony);
}
NS_ASSERTION(gTelephonyList, "This should never be null!");
NS_ASSERTION(gTelephonyList->Contains(this), "Should be in the list!");
@ -115,6 +110,12 @@ Telephony::~Telephony()
}
}
JSObject*
Telephony::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return TelephonyBinding::Wrap(aCx, aScope, this);
}
// static
already_AddRefed<Telephony>
Telephony::Create(nsPIDOMWindow* aOwner, ErrorResult& aRv)
@ -146,6 +147,7 @@ Telephony::Create(nsPIDOMWindow* aOwner, ErrorResult& aRv)
telephony->mProvider = ril;
telephony->mListener = new Listener(telephony);
telephony->mCallsList = new CallsList(telephony);
nsresult rv = ril->EnumerateCalls(telephony->mListener);
if (NS_FAILED(rv)) {
@ -199,12 +201,15 @@ Telephony::NotifyCallsChanged(TelephonyCall* aCall)
return DispatchCallEvent(NS_LITERAL_STRING("callschanged"), aCall);
}
nsresult
already_AddRefed<TelephonyCall>
Telephony::DialInternal(bool isEmergency,
const nsAString& aNumber,
nsIDOMTelephonyCall** aResult)
ErrorResult& aRv)
{
NS_ENSURE_ARG(!aNumber.IsEmpty());
if (aNumber.IsEmpty()) {
aRv.Throw(NS_ERROR_INVALID_ARG);
return nullptr;
}
for (uint32_t index = 0; index < mCalls.Length(); index++) {
const nsRefPtr<TelephonyCall>& tempCall = mCalls[index];
@ -213,7 +218,8 @@ Telephony::DialInternal(bool isEmergency,
// One call has been dialed already and we only support one outgoing call
// at a time.
NS_WARNING("Only permitted to dial one call at a time!");
return NS_ERROR_NOT_AVAILABLE;
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
}
@ -223,7 +229,10 @@ Telephony::DialInternal(bool isEmergency,
} else {
rv = mProvider->Dial(aNumber);
}
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
}
nsRefPtr<TelephonyCall> call = CreateNewDialingCall(aNumber);
@ -236,169 +245,114 @@ Telephony::DialInternal(bool isEmergency,
}
}
call.forget(aResult);
return NS_OK;
return call.forget();
}
NS_IMPL_CYCLE_COLLECTION_CLASS(Telephony)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Telephony,
nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
for (uint32_t index = 0; index < tmp->mCalls.Length(); index++) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCalls[i]");
cb.NoteXPCOMChild(tmp->mCalls[index]->ToISupports());
}
// Don't traverse mListener because it doesn't keep any reference to
// Telephony but a raw pointer instead.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCalls)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallsList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Telephony,
nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCallsArray)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Telephony,
nsDOMEventTargetHelper)
tmp->mCalls.Clear();
tmp->mActiveCall = nullptr;
tmp->mCallsArray = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCalls)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallsList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Telephony)
NS_INTERFACE_MAP_ENTRY(nsIDOMTelephony)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Telephony)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(Telephony, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(Telephony, nsDOMEventTargetHelper)
DOMCI_DATA(Telephony, Telephony)
NS_IMPL_ISUPPORTS1(Telephony::Listener, nsITelephonyListener)
// nsIDOMTelephony
// Telephony WebIDL
NS_IMETHODIMP
Telephony::Dial(const nsAString& aNumber, nsIDOMTelephonyCall** aResult)
already_AddRefed<TelephonyCall>
Telephony::Dial(const nsAString& aNumber, ErrorResult& aRv)
{
DialInternal(false, aNumber, aResult);
return NS_OK;
nsRefPtr<TelephonyCall> call = DialInternal(false, aNumber, aRv);
return call.forget();
}
NS_IMETHODIMP
Telephony::DialEmergency(const nsAString& aNumber, nsIDOMTelephonyCall** aResult)
already_AddRefed<TelephonyCall>
Telephony::DialEmergency(const nsAString& aNumber, ErrorResult& aRv)
{
DialInternal(true, aNumber, aResult);
return NS_OK;
nsRefPtr<TelephonyCall> call = DialInternal(true, aNumber, aRv);
return call.forget();
}
NS_IMETHODIMP
Telephony::GetMuted(bool* aMuted)
bool
Telephony::GetMuted(ErrorResult& aRv) const
{
nsresult rv = mProvider->GetMicrophoneMuted(aMuted);
NS_ENSURE_SUCCESS(rv, rv);
bool muted = false;
aRv = mProvider->GetMicrophoneMuted(&muted);
return NS_OK;
return muted;
}
NS_IMETHODIMP
Telephony::SetMuted(bool aMuted)
void
Telephony::SetMuted(bool aMuted, ErrorResult& aRv)
{
nsresult rv = mProvider->SetMicrophoneMuted(aMuted);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
aRv = mProvider->SetMicrophoneMuted(aMuted);
}
NS_IMETHODIMP
Telephony::GetSpeakerEnabled(bool* aSpeakerEnabled)
bool
Telephony::GetSpeakerEnabled(ErrorResult& aRv) const
{
nsresult rv = mProvider->GetSpeakerEnabled(aSpeakerEnabled);
NS_ENSURE_SUCCESS(rv, rv);
bool enabled = false;
aRv = mProvider->GetSpeakerEnabled(&enabled);
return NS_OK;
return enabled;
}
NS_IMETHODIMP
Telephony::SetSpeakerEnabled(bool aSpeakerEnabled)
void
Telephony::SetSpeakerEnabled(bool aEnabled, ErrorResult& aRv)
{
nsresult rv = mProvider->SetSpeakerEnabled(aSpeakerEnabled);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
aRv = mProvider->SetSpeakerEnabled(aEnabled);
}
NS_IMETHODIMP
Telephony::GetActive(nsIDOMTelephonyCall** aActive)
already_AddRefed<TelephonyCall>
Telephony::GetActive() const
{
nsCOMPtr<nsIDOMTelephonyCall> activeCall = mActiveCall;
activeCall.forget(aActive);
return NS_OK;
nsCOMPtr<TelephonyCall> activeCall = mActiveCall;
return activeCall.forget();
}
NS_IMETHODIMP
Telephony::GetCalls(JS::Value* aCalls)
already_AddRefed<CallsList>
Telephony::Calls() const
{
JSObject* calls = mCallsArray;
if (!calls) {
nsresult rv;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_SUCCESS(rv, rv);
AutoPushJSContext cx(sc ? sc->GetNativeContext() : nullptr);
if (sc) {
rv = nsTArrayToJSArray(cx, mCalls, &calls);
NS_ENSURE_SUCCESS(rv, rv);
if (!mRooted) {
NS_HOLD_JS_OBJECTS(this, Telephony);
mRooted = true;
}
mCallsArray = calls;
} else {
NS_ENSURE_SUCCESS(rv, rv);
}
}
aCalls->setObject(*calls);
return NS_OK;
nsRefPtr<CallsList> list = mCallsList;
return list.forget();
}
NS_IMETHODIMP
Telephony::StartTone(const nsAString& aDTMFChar)
void
Telephony::StartTone(const nsAString& aDTMFChar, ErrorResult& aRv)
{
if (aDTMFChar.IsEmpty()) {
NS_WARNING("Empty tone string will be ignored");
return NS_OK;
return;
}
if (aDTMFChar.Length() > 1) {
return NS_ERROR_INVALID_ARG;
aRv.Throw(NS_ERROR_INVALID_ARG);
return;
}
nsresult rv = mProvider->StartTone(aDTMFChar);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
aRv = mProvider->StartTone(aDTMFChar);
}
NS_IMETHODIMP
Telephony::StopTone()
void
Telephony::StopTone(ErrorResult& aRv)
{
nsresult rv = mProvider->StopTone();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
aRv = mProvider->StopTone();
}
NS_IMPL_EVENT_HANDLER(Telephony, incoming)
NS_IMPL_EVENT_HANDLER(Telephony, callschanged)
NS_IMPL_EVENT_HANDLER(Telephony, remoteheld)
NS_IMPL_EVENT_HANDLER(Telephony, remoteresumed)
// EventTarget
void
@ -598,9 +552,21 @@ Telephony::NotifyError(int32_t aCallIndex,
return NS_OK;
}
NS_IMETHODIMP
Telephony::NotifyCdmaCallWaiting(const nsAString& aNumber)
{
MOZ_ASSERT(mActiveCall &&
mActiveCall->CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED);
nsRefPtr<TelephonyCall> callToNotify = mActiveCall;
callToNotify->UpdateSecondNumber(aNumber);
DispatchCallEvent(NS_LITERAL_STRING("callschanged"), callToNotify);
return NS_OK;
}
nsresult
Telephony::DispatchCallEvent(const nsAString& aType,
nsIDOMTelephonyCall* aCall)
TelephonyCall* aCall)
{
// The call may be null in following cases:
// 1. callschanged when notifying enumeration being completed
@ -610,16 +576,9 @@ Telephony::DispatchCallEvent(const nsAString& aType,
aType.EqualsLiteral("remoteheld") ||
aType.EqualsLiteral("remtoeresumed"));
nsCOMPtr<nsIDOMEvent> event;
NS_NewDOMCallEvent(getter_AddRefs(event), this, nullptr, nullptr);
NS_ASSERTION(event, "This should never fail!");
nsRefPtr<CallEvent> event = CallEvent::Create(this, aType, aCall, false, false);
nsCOMPtr<nsIDOMCallEvent> callEvent = do_QueryInterface(event);
MOZ_ASSERT(callEvent);
nsresult rv = callEvent->InitCallEvent(aType, false, false, aCall);
NS_ENSURE_SUCCESS(rv, rv);
return DispatchTrustedEvent(callEvent);
return DispatchTrustedEvent(event);
}
void

View File

@ -12,17 +12,13 @@
// assume they see the definition of TelephonyCall.
#include "TelephonyCall.h"
#include "nsIDOMTelephony.h"
#include "nsIDOMTelephonyCall.h"
#include "nsITelephonyProvider.h"
class nsIScriptContext;
class nsPIDOMWindow;
BEGIN_TELEPHONY_NAMESPACE
class Telephony : public nsDOMEventTargetHelper,
public nsIDOMTelephony
class Telephony MOZ_FINAL : public nsDOMEventTargetHelper
{
/**
* Class Telephony doesn't actually inherit nsITelephonyListener.
@ -41,40 +37,73 @@ class Telephony : public nsDOMEventTargetHelper,
TelephonyCall* mActiveCall;
nsTArray<nsRefPtr<TelephonyCall> > mCalls;
nsRefPtr<CallsList> mCallsList;
// Cached calls array object. Cleared whenever mCalls changes and then rebuilt
// once a page looks for the liveCalls attribute.
JS::Heap<JSObject*> mCallsArray;
bool mRooted;
bool mEnumerated;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMTELEPHONY
NS_DECL_NSITELEPHONYLISTENER
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
Telephony,
nsDOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Telephony,
nsDOMEventTargetHelper)
nsPIDOMWindow*
GetParentObject() const
{
return GetOwner();
}
// WrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
// WebIDL
already_AddRefed<TelephonyCall>
Dial(const nsAString& aNumber, ErrorResult& aRv);
already_AddRefed<TelephonyCall>
DialEmergency(const nsAString& aNumber, ErrorResult& aRv);
bool
GetMuted(ErrorResult& aRv) const;
void
SetMuted(bool aMuted, ErrorResult& aRv);
bool
GetSpeakerEnabled(ErrorResult& aRv) const;
void
SetSpeakerEnabled(bool aEnabled, ErrorResult& aRv);
already_AddRefed<TelephonyCall>
GetActive() const;
already_AddRefed<CallsList>
Calls() const;
void
StartTone(const nsAString& aDTMF, ErrorResult& aRv);
void
StopTone(ErrorResult& aRv);
IMPL_EVENT_HANDLER(incoming)
IMPL_EVENT_HANDLER(callschanged)
IMPL_EVENT_HANDLER(remoteheld)
IMPL_EVENT_HANDLER(remoteresumed)
static already_AddRefed<Telephony>
Create(nsPIDOMWindow* aOwner, ErrorResult& aRv);
static bool CheckPermission(nsPIDOMWindow* aOwner);
nsISupports*
ToISupports()
{
return static_cast<EventTarget*>(this);
}
void
AddCall(TelephonyCall* aCall)
{
NS_ASSERTION(!mCalls.Contains(aCall), "Already know about this one!");
mCalls.AppendElement(aCall);
mCallsArray = nullptr;
NotifyCallsChanged(aCall);
}
@ -83,7 +112,6 @@ public:
{
NS_ASSERTION(mCalls.Contains(aCall), "Didn't know about this one!");
mCalls.RemoveElement(aCall);
mCallsArray = nullptr;
NotifyCallsChanged(aCall);
}
@ -93,6 +121,12 @@ public:
return mProvider;
}
const nsTArray<nsRefPtr<TelephonyCall> >&
CallsArray() const
{
return mCalls;
}
virtual void EventListenerAdded(nsIAtom* aType) MOZ_OVERRIDE;
private:
@ -108,14 +142,14 @@ private:
nsresult
NotifyCallsChanged(TelephonyCall* aCall);
nsresult
already_AddRefed<TelephonyCall>
DialInternal(bool isEmergency,
const nsAString& aNumber,
nsIDOMTelephonyCall** aResult);
ErrorResult& aRv);
nsresult
DispatchCallEvent(const nsAString& aType,
nsIDOMTelephonyCall* aCall);
TelephonyCall* aCall);
void
EnqueueEnumerationAck();

View File

@ -5,16 +5,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TelephonyCall.h"
#include "nsIDOMCallEvent.h"
#include "mozilla/dom/TelephonyCallBinding.h"
#include "mozilla/dom/DOMError.h"
#include "GeneratedEvents.h"
#include "nsDOMClassInfo.h"
#include "CallEvent.h"
#include "Telephony.h"
#include "nsITelephonyProvider.h"
USING_TELEPHONY_NAMESPACE
using namespace mozilla::dom;
// static
already_AddRefed<TelephonyCall>
@ -46,6 +45,17 @@ TelephonyCall::TelephonyCall()
mLive(false),
mOutgoing(false)
{
SetIsDOMBinding();
}
TelephonyCall::~TelephonyCall()
{
}
JSObject*
TelephonyCall::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return TelephonyCallBinding::Wrap(aCx, aScope, this);
}
void
@ -124,20 +134,13 @@ TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents)
nsresult
TelephonyCall::DispatchCallEvent(const nsAString& aType,
nsIDOMTelephonyCall* aCall)
TelephonyCall* aCall)
{
MOZ_ASSERT(aCall);
nsCOMPtr<nsIDOMEvent> event;
NS_NewDOMCallEvent(getter_AddRefs(event), this, nullptr, nullptr);
NS_ASSERTION(event, "This should never fail!");
nsRefPtr<CallEvent> event = CallEvent::Create(this, aType, aCall, false, false);
nsCOMPtr<nsIDOMCallEvent> callEvent = do_QueryInterface(event);
MOZ_ASSERT(callEvent);
nsresult rv = callEvent->InitCallEvent(aType, false, false, aCall);
NS_ENSURE_SUCCESS(rv, rv);
return DispatchTrustedEvent(callEvent);
return DispatchTrustedEvent(event);
}
void
@ -163,114 +166,87 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED_2(TelephonyCall,
mError);
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TelephonyCall)
NS_INTERFACE_MAP_ENTRY(nsIDOMTelephonyCall)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TelephonyCall)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(TelephonyCall, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(TelephonyCall, nsDOMEventTargetHelper)
DOMCI_DATA(TelephonyCall, TelephonyCall)
// TelephonyCall WebIDL
NS_IMETHODIMP
TelephonyCall::GetNumber(nsAString& aNumber)
already_AddRefed<DOMError>
TelephonyCall::GetError() const
{
aNumber.Assign(mNumber);
return NS_OK;
nsRefPtr<DOMError> error = mError;
return error.forget();
}
NS_IMETHODIMP
TelephonyCall::GetState(nsAString& aState)
{
aState.Assign(mState);
return NS_OK;
}
NS_IMETHODIMP
TelephonyCall::GetEmergency(bool* aEmergency)
{
*aEmergency = mEmergency;
return NS_OK;
}
NS_IMETHODIMP
TelephonyCall::GetError(nsISupports** aError)
{
NS_IF_ADDREF(*aError = mError);
return NS_OK;
}
NS_IMETHODIMP
TelephonyCall::Answer()
void
TelephonyCall::Answer(ErrorResult& aRv)
{
if (mCallState != nsITelephonyProvider::CALL_STATE_INCOMING) {
NS_WARNING("Answer on non-incoming call ignored!");
return NS_OK;
return;
}
nsresult rv = mTelephony->Provider()->AnswerCall(mCallIndex);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
ChangeStateInternal(nsITelephonyProvider::CALL_STATE_CONNECTING, true);
return NS_OK;
}
NS_IMETHODIMP
TelephonyCall::HangUp()
void
TelephonyCall::HangUp(ErrorResult& aRv)
{
if (mCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTING ||
mCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
NS_WARNING("HangUp on previously disconnected call ignored!");
return NS_OK;
return;
}
nsresult rv = mCallState == nsITelephonyProvider::CALL_STATE_INCOMING ?
mTelephony->Provider()->RejectCall(mCallIndex) :
mTelephony->Provider()->HangUp(mCallIndex);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
ChangeStateInternal(nsITelephonyProvider::CALL_STATE_DISCONNECTING, true);
return NS_OK;
}
NS_IMETHODIMP
TelephonyCall::Hold()
void
TelephonyCall::Hold(ErrorResult& aRv)
{
if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) {
NS_WARNING("Hold non-connected call ignored!");
return NS_OK;
return;
}
nsresult rv = mTelephony->Provider()->HoldCall(mCallIndex);
NS_ENSURE_SUCCESS(rv,rv);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
ChangeStateInternal(nsITelephonyProvider::CALL_STATE_HOLDING, true);
return NS_OK;
}
NS_IMETHODIMP
TelephonyCall::Resume()
void
TelephonyCall::Resume(ErrorResult& aRv)
{
if (mCallState != nsITelephonyProvider::CALL_STATE_HELD) {
NS_WARNING("Resume non-held call ignored!");
return NS_OK;
return;
}
nsresult rv = mTelephony->Provider()->ResumeCall(mCallIndex);
NS_ENSURE_SUCCESS(rv,rv);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
ChangeStateInternal(nsITelephonyProvider::CALL_STATE_RESUMING, true);
return NS_OK;
}
NS_IMPL_EVENT_HANDLER(TelephonyCall, statechange)
NS_IMPL_EVENT_HANDLER(TelephonyCall, dialing)
NS_IMPL_EVENT_HANDLER(TelephonyCall, alerting)
NS_IMPL_EVENT_HANDLER(TelephonyCall, connecting)
NS_IMPL_EVENT_HANDLER(TelephonyCall, connected)
NS_IMPL_EVENT_HANDLER(TelephonyCall, disconnecting)
NS_IMPL_EVENT_HANDLER(TelephonyCall, disconnected)
NS_IMPL_EVENT_HANDLER(TelephonyCall, holding)
NS_IMPL_EVENT_HANDLER(TelephonyCall, held)
NS_IMPL_EVENT_HANDLER(TelephonyCall, resuming)
NS_IMPL_EVENT_HANDLER(TelephonyCall, error)

View File

@ -9,19 +9,18 @@
#include "TelephonyCommon.h"
#include "nsIDOMTelephonyCall.h"
#include "mozilla/dom/DOMError.h"
class nsPIDOMWindow;
BEGIN_TELEPHONY_NAMESPACE
class TelephonyCall : public nsDOMEventTargetHelper,
public nsIDOMTelephonyCall
class TelephonyCall MOZ_FINAL : public nsDOMEventTargetHelper
{
nsRefPtr<Telephony> mTelephony;
nsString mNumber;
nsString mSecondNumber;
nsString mState;
bool mEmergency;
nsRefPtr<mozilla::dom::DOMError> mError;
@ -33,22 +32,77 @@ class TelephonyCall : public nsDOMEventTargetHelper,
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMTELEPHONYCALL
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TelephonyCall,
nsDOMEventTargetHelper)
nsPIDOMWindow*
GetParentObject() const
{
return GetOwner();
}
// WrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
// WebIDL
void
GetNumber(nsString& aNumber) const
{
aNumber.Assign(mNumber);
}
void
GetSecondNumber(nsString& aSecondNumber) const
{
aSecondNumber.Assign(mSecondNumber);
}
void
GetState(nsString& aState) const
{
aState.Assign(mState);
}
bool
Emergency() const
{
return mEmergency;
}
already_AddRefed<DOMError>
GetError() const;
void
Answer(ErrorResult& aRv);
void
HangUp(ErrorResult& aRv);
void
Hold(ErrorResult& aRv);
void
Resume(ErrorResult& aRv);
IMPL_EVENT_HANDLER(statechange)
IMPL_EVENT_HANDLER(dialing)
IMPL_EVENT_HANDLER(alerting)
IMPL_EVENT_HANDLER(connecting)
IMPL_EVENT_HANDLER(connected)
IMPL_EVENT_HANDLER(disconnecting)
IMPL_EVENT_HANDLER(disconnected)
IMPL_EVENT_HANDLER(holding)
IMPL_EVENT_HANDLER(held)
IMPL_EVENT_HANDLER(resuming)
IMPL_EVENT_HANDLER(error)
static already_AddRefed<TelephonyCall>
Create(Telephony* aTelephony, const nsAString& aNumber, uint16_t aCallState,
uint32_t aCallIndex = kOutgoingPlaceholderCallIndex,
bool aEmergency = false);
nsISupports*
ToISupports()
{
return static_cast<EventTarget*>(this);
}
void
ChangeState(uint16_t aCallState)
{
@ -81,6 +135,12 @@ public:
mEmergency = aEmergency;
}
void
UpdateSecondNumber(const nsAString& aNumber)
{
mSecondNumber = aNumber;
}
bool
IsOutgoing() const
{
@ -93,15 +153,14 @@ public:
private:
TelephonyCall();
~TelephonyCall()
{ }
~TelephonyCall();
void
ChangeStateInternal(uint16_t aCallState, bool aFireEvents);
nsresult
DispatchCallEvent(const nsAString& aType,
nsIDOMTelephonyCall* aCall);
TelephonyCall* aCall);
};
END_TELEPHONY_NAMESPACE

View File

@ -7,6 +7,8 @@
#ifndef mozilla_dom_telephony_telephonycommon_h__
#define mozilla_dom_telephony_telephonycommon_h__
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
@ -22,15 +24,13 @@
#define USING_TELEPHONY_NAMESPACE \
using namespace mozilla::dom::telephony;
class nsIDOMTelephony;
class nsIDOMTelephonyCall;
BEGIN_TELEPHONY_NAMESPACE
enum {
kOutgoingPlaceholderCallIndex = UINT32_MAX
};
class CallsList;
class Telephony;
class TelephonyCall;

View File

@ -5,9 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIDOMCallEvent.idl',
'nsIDOMTelephony.idl',
'nsIDOMTelephonyCall.idl',
'nsITelephonyProvider.idl',
]
@ -22,6 +19,8 @@ XPIDL_FLAGS += [
MODULE = 'dom'
CPP_SOURCES += [
'CallEvent.cpp',
'CallsList.cpp',
'Telephony.cpp',
'TelephonyCall.cpp',
]

View File

@ -1,24 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* 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 "nsIDOMEvent.idl"
interface nsIDOMTelephonyCall;
[scriptable, builtinclass, uuid(476aacec-661e-44ec-80b4-4b7292b927b5)]
interface nsIDOMCallEvent : nsIDOMEvent
{
readonly attribute nsIDOMTelephonyCall call;
[noscript] void initCallEvent(in DOMString aType,
in boolean aCanBubble,
in boolean aCancelable,
in nsIDOMTelephonyCall aCall);
};
dictionary CallEventInit : EventInit
{
nsIDOMTelephonyCall call;
};

View File

@ -1,35 +0,0 @@
/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIDOMEventTarget.idl"
interface nsIDOMEventListener;
interface nsIDOMTelephonyCall;
[scriptable, builtinclass, uuid(5ad8bf8b-958c-447b-9e1a-e6cf598b680f)]
interface nsIDOMTelephony : nsIDOMEventTarget
{
nsIDOMTelephonyCall dial(in DOMString number);
nsIDOMTelephonyCall dialEmergency(in DOMString number);
attribute boolean muted;
attribute boolean speakerEnabled;
// The call that is "active", i.e. receives microphone input and tones
// generated via startTone.
readonly attribute nsIDOMTelephonyCall active;
// Array of all calls that are currently connected.
readonly attribute jsval calls;
void startTone(in DOMString tone);
void stopTone();
[implicit_jscontext] attribute jsval onincoming;
[implicit_jscontext] attribute jsval oncallschanged;
[implicit_jscontext] attribute jsval onremoteheld;
[implicit_jscontext] attribute jsval onremoteresumed;
};

View File

@ -1,44 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* 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 "nsIDOMEventTarget.idl"
interface nsIDOMEventListener;
[scriptable, builtinclass, uuid(74d240f5-a379-4ac0-a085-a7f714189a27)]
interface nsIDOMTelephonyCall : nsIDOMEventTarget
{
readonly attribute DOMString number;
readonly attribute DOMString state;
// The property "emergency" indicate whether the call number is an emergency
// number. Only the outgoing call could have a value with true and it is
// available after dialing state.
readonly attribute boolean emergency;
// This is a DOMError
readonly attribute nsISupports error;
void answer();
void hangUp();
void hold();
void resume();
[implicit_jscontext] attribute jsval onstatechange;
[implicit_jscontext] attribute jsval ondialing;
[implicit_jscontext] attribute jsval onalerting;
[implicit_jscontext] attribute jsval onconnecting;
[implicit_jscontext] attribute jsval onconnected;
[implicit_jscontext] attribute jsval ondisconnecting;
[implicit_jscontext] attribute jsval ondisconnected;
[implicit_jscontext] attribute jsval onholding;
[implicit_jscontext] attribute jsval onheld;
[implicit_jscontext] attribute jsval onresuming;
[implicit_jscontext] attribute jsval onerror;
};

View File

@ -4,7 +4,7 @@
#include "nsISupports.idl"
[scriptable, uuid(a1e9fdd9-7901-4a0f-8b6b-6ee0fa8f9d81)]
[scriptable, uuid(3fb573c3-6fc4-41d3-80c1-f0e60662691e)]
interface nsITelephonyListener : nsISupports
{
/**
@ -82,6 +82,14 @@ interface nsITelephonyListener : nsISupports
*/
void notifyError(in long callIndex,
in AString error);
/**
* Called when a waiting call comes in CDMA networks.
*
* @param number
* Number of the other party.
*/
void notifyCdmaCallWaiting(in AString number);
};
/**

View File

@ -8,7 +8,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
Cu.import("resource://gre/modules/WspPduHelper.jsm", this);
const DEBUG = false; // set to true to see debug messages
@ -109,7 +109,14 @@ this.WapPushManager = {
};
}
let sender = PhoneNumberUtils.normalize(options.sourceAddress, false);
let parsedSender = PhoneNumberUtils.parse(sender);
if (parsedSender && parsedSender.internationalNumber) {
sender = parsedSender.internationalNumber;
}
gSystemMessenger.broadcastMessage("wappush-received", {
sender: sender,
contentType: msg.contentType,
content: msg.content
});

View File

@ -3,9 +3,8 @@
* 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/.
*/
interface TelephonyCall;
[Constructor(DOMString type, optional CallEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
[Constructor(DOMString type, optional CallEventInit eventInitDict)]
interface CallEvent : Event
{
readonly attribute TelephonyCall? call;

View File

@ -0,0 +1,11 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
[ArrayClass, NoInterfaceObject]
interface CallsList {
getter TelephonyCall item(unsigned long index);
readonly attribute unsigned long length;
};

View File

@ -252,11 +252,9 @@ partial interface Navigator {
};
#ifdef MOZ_B2G_RIL
interface MozTelephony;
// nsIDOMNavigatorTelephony
partial interface Navigator {
[Throws, Func="Navigator::HasTelephonySupport"]
readonly attribute MozTelephony? mozTelephony;
readonly attribute Telephony? mozTelephony;
};
// nsIMozNavigatorMobileConnection

View File

@ -0,0 +1,35 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
interface Telephony : EventTarget {
[Throws]
TelephonyCall dial(DOMString number);
[Throws]
TelephonyCall dialEmergency(DOMString number);
[Throws]
attribute boolean muted;
[Throws]
attribute boolean speakerEnabled;
readonly attribute TelephonyCall? active;
readonly attribute CallsList calls;
[Throws]
void startTone(DOMString tone);
[Throws]
void stopTone();
[SetterThrows]
attribute EventHandler onincoming;
[SetterThrows]
attribute EventHandler oncallschanged;
[SetterThrows]
attribute EventHandler onremoteheld;
[SetterThrows]
attribute EventHandler onremoteresumed;
};

View File

@ -0,0 +1,54 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
interface TelephonyCall : EventTarget {
readonly attribute DOMString number;
// In CDMA networks, the 2nd waiting call shares the connection with the 1st
// call. We need an additional attribute for the 2nd number.
readonly attribute DOMString? secondNumber;
readonly attribute DOMString state;
// The property "emergency" indicates whether the call number is an emergency
// number. Only the outgoing call could have a value with true and it is
// available after dialing state.
readonly attribute boolean emergency;
readonly attribute DOMError? error;
[Throws]
void answer();
[Throws]
void hangUp();
[Throws]
void hold();
[Throws]
void resume();
[SetterThrows]
attribute EventHandler onstatechange;
[SetterThrows]
attribute EventHandler ondialing;
[SetterThrows]
attribute EventHandler onalerting;
[SetterThrows]
attribute EventHandler onconnecting;
[SetterThrows]
attribute EventHandler onconnected;
[SetterThrows]
attribute EventHandler ondisconnecting;
[SetterThrows]
attribute EventHandler ondisconnected;
[SetterThrows]
attribute EventHandler onholding;
[SetterThrows]
attribute EventHandler onheld;
[SetterThrows]
attribute EventHandler onresuming;
[SetterThrows]
attribute EventHandler onerror;
};

View File

@ -437,7 +437,10 @@ endif
ifdef MOZ_B2G_RIL
webidl_files += \
CallsList.webidl \
MozStkCommandEvent.webidl \
Telephony.webidl \
TelephonyCall.webidl \
$(NULL)
endif

View File

@ -843,6 +843,7 @@ var WifiManager = (function() {
if (ok === 0) {
// Tell the event worker to start waiting for events.
retryTimer = null;
connectTries = 0;
didConnectSupplicant(function(){});
return;
}
@ -858,6 +859,7 @@ var WifiManager = (function() {
}
retryTimer = null;
connectTries = 0;
notify("supplicantlost", { success: false });
}

View File

@ -32,7 +32,6 @@ simple_events = [
'BluetoothStatusChangedEvent',
#endif
#ifdef MOZ_B2G_RIL
'CallEvent',
'CFStateChangeEvent',
'DataErrorEvent',
'IccCardLockErrorEvent',

View File

@ -21,6 +21,7 @@
"content/media/test/test_can_play_type.html":"timed out",
"content/media/test/test_can_play_type_mpeg.html":"7 failures out of 27",
"content/media/test/test_can_play_type_no_dash.html":"",
"content/media/test/test_mediarecorder_record_stopms.html":"",
"content/media/test/test_can_play_type_ogg.html":"",
"content/media/test/test_chaining.html": "timed out",
"content/media/test/test_delay_load.html": "6 failures",

View File

@ -15,6 +15,7 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/WebappOSUtils.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
this.WebappsInstaller = {
shell: null,
@ -639,6 +640,7 @@ MacNativeApp.prototype = {
writer.setString("Webapp", "Name", this.appName);
writer.setString("Webapp", "Profile", this.appProfileDir.leafName);
writer.writeFile();
applicationINI.permissions = FileUtils.PERMS_FILE;
// ${InstallDir}/Contents/Info.plist
let infoPListContent = '<?xml version="1.0" encoding="UTF-8"?>\n\
@ -956,9 +958,12 @@ LinuxNativeApp.prototype = {
* @param aData a string with the data to be written
*/
function writeToFile(aFile, aData) {
let path = aFile.path;
let data = new TextEncoder().encode(aData);
return OS.File.writeAtomic(path, data, { tmpPath: path + ".tmp" });
return Task.spawn(function() {
let data = new TextEncoder().encode(aData);
let file = yield OS.File.open(aFile.path, { truncate: true }, { unixMode: FileUtils.PERMS_FILE });
yield file.write(data);
yield file.close();
});
}
/**

View File

@ -128,15 +128,22 @@ function updateMenuItems() {
#endif
}
#ifndef XP_MACOSX
let gEditUIVisible = true;
#endif
function updateEditUIVisibility() {
#ifndef XP_MACOSX
let editMenuPopupState = document.getElementById("menu_EditPopup").state;
let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
// The UI is visible if the Edit menu is opening or open, if the context menu
// is open, or if the toolbar has been customized to include the Cut, Copy,
// or Paste toolbar buttons.
gEditUIVisible = editMenuPopupState == "showing" ||
editMenuPopupState == "open";
editMenuPopupState == "open" ||
contextMenuPopupState == "showing" ||
contextMenuPopupState == "open";
// If UI is visible, update the edit commands' enabled state to reflect
// whether or not they are actually enabled for the current focus/selection.
@ -177,3 +184,60 @@ function updateCrashReportURL(aURI) {
gCrashReporter.annotateCrashReport("URL", uri.spec);
#endif
}
// Context menu handling code.
// At the moment there isn't any built-in menu, we only support HTML5 custom
// menus.
let gContextMenu = null;
XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
let tmp = {};
Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
return new tmp.PageMenu();
});
function showContextMenu(aEvent, aXULMenu) {
if (aEvent.target != aXULMenu) {
return true;
}
gContextMenu = new nsContextMenu(aXULMenu);
if (gContextMenu.shouldDisplay) {
updateEditUIVisibility();
}
return gContextMenu.shouldDisplay;
}
function hideContextMenu(aEvent, aXULMenu) {
if (aEvent.target != aXULMenu) {
return;
}
gContextMenu = null;
updateEditUIVisibility();
}
function nsContextMenu(aXULMenu) {
this.initMenu(aXULMenu);
}
nsContextMenu.prototype = {
initMenu: function(aXULMenu) {
this.hasPageMenu = PageMenu.maybeBuildAndAttachMenu(document.popupNode,
aXULMenu);
this.shouldDisplay = this.hasPageMenu;
this.showItem("page-menu-separator", this.hasPageMenu);
},
showItem: function(aItemOrID, aShow) {
let item = aItemOrID.constructor == String ?
document.getElementById(aItemOrID) : aItemOrID;
if (item) {
item.hidden = !aShow;
}
}
};

View File

@ -154,6 +154,14 @@
</menu>
</menubar>
<browser type="content-primary" id="content" flex="1"/>
<browser type="content-primary" id="content" flex="1" context="contentAreaContextMenu" />
<popupset>
<menuseparator id="page-menu-separator"/>
<menupopup id="contentAreaContextMenu" pagemenu="start"
onpopupshowing="return showContextMenu(event, this)"
onpopuphiding="hideContextMenu(event, this)">
</menupopup>
</popupset>
</window>

View File

@ -331,14 +331,12 @@ NSString
@"org.mozilla.aurora",
@"org.mozilla.firefox", nil];
//if they provided a manual override, use that. If they made an error, it will fail to launch
// If they provided a binary ID, use that.
if (alternateBinaryID != nil && ([alternateBinaryID length] > 0)) {
binaryPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:alternateBinaryID];
if (binaryPath == nil || [binaryPath length] == 0) {
@throw MakeException(@"Web Runtime Not Found",
[NSString stringWithFormat:@"Failed to locate specified override Web Runtime with signature '%@'", alternateBinaryID]);
if (binaryPath && [binaryPath length] > 0) {
return binaryPath;
}
return binaryPath;
}
//No override found, loop through the various flavors of firefox we have