mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge b2g-inbound to m-c.
This commit is contained in:
commit
c56b4e5a84
@ -17,6 +17,7 @@ html xul|scrollbar {
|
||||
background-image: none !important;
|
||||
border: 0px solid transparent !important;
|
||||
z-index: 2147483647;
|
||||
pointer-events: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]);
|
||||
|
@ -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();
|
||||
});
|
||||
},
|
||||
|
@ -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"
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "fd03fbd18a09517bc5eb4e2af62314421ae7124a",
|
||||
"revision": "0e3e30e489ff38cceb8b9cf9ee5caea5fb072457",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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())
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
487
content/media/omx/MP3FrameParser.cpp
Normal file
487
content/media/omx/MP3FrameParser.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
76
content/media/omx/MP3FrameParser.h
Normal file
76
content/media/omx/MP3FrameParser.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -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.");
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ EXPORTS += [
|
||||
CPP_SOURCES += [
|
||||
'MediaOmxDecoder.cpp',
|
||||
'MediaOmxReader.cpp',
|
||||
'MP3FrameParser.cpp',
|
||||
'OmxDecoder.cpp',
|
||||
'OMXCodecProxy.cpp',
|
||||
]
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
47
content/media/test/test_mediarecorder_record_stopms.html
Normal file
47
content/media/test/test_mediarecorder_record_stopms.html
Normal 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
BIN
content/media/test/vbr.mp3
Normal file
Binary file not shown.
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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) {
|
||||
|
@ -1140,7 +1140,7 @@ Navigator::GetMozCellBroadcast(ErrorResult& aRv)
|
||||
return mCellBroadcast;
|
||||
}
|
||||
|
||||
nsIDOMTelephony*
|
||||
telephony::Telephony*
|
||||
Navigator::GetMozTelephony(ErrorResult& aRv)
|
||||
{
|
||||
if (!mTelephony) {
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -22,7 +22,7 @@ var gData = [
|
||||
perm: ["telephony"],
|
||||
needParentPerm: true,
|
||||
obj: "mozTelephony",
|
||||
idl: "nsIDOMTelephony",
|
||||
webidl: "Telephony",
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
73
dom/telephony/CallEvent.cpp
Normal file
73
dom/telephony/CallEvent.cpp
Normal 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
61
dom/telephony/CallEvent.h
Normal 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
|
69
dom/telephony/CallsList.cpp
Normal file
69
dom/telephony/CallsList.cpp
Normal 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
50
dom/telephony/CallsList.h
Normal 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__
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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;
|
||||
};
|
@ -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;
|
||||
};
|
@ -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;
|
||||
};
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -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;
|
||||
|
11
dom/webidl/CallsList.webidl
Normal file
11
dom/webidl/CallsList.webidl
Normal 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;
|
||||
};
|
@ -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
|
||||
|
35
dom/webidl/Telephony.webidl
Normal file
35
dom/webidl/Telephony.webidl
Normal 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;
|
||||
};
|
54
dom/webidl/TelephonyCall.webidl
Normal file
54
dom/webidl/TelephonyCall.webidl
Normal 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;
|
||||
};
|
@ -437,7 +437,10 @@ endif
|
||||
|
||||
ifdef MOZ_B2G_RIL
|
||||
webidl_files += \
|
||||
CallsList.webidl \
|
||||
MozStkCommandEvent.webidl \
|
||||
Telephony.webidl \
|
||||
TelephonyCall.webidl \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
@ -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 });
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,6 @@ simple_events = [
|
||||
'BluetoothStatusChangedEvent',
|
||||
#endif
|
||||
#ifdef MOZ_B2G_RIL
|
||||
'CallEvent',
|
||||
'CFStateChangeEvent',
|
||||
'DataErrorEvent',
|
||||
'IccCardLockErrorEvent',
|
||||
|
@ -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",
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user