Merge latest green inbound changeset and mozilla-central

This commit is contained in:
Ed Morley 2013-09-04 12:54:46 +01:00
commit 2a2f5c9d13
101 changed files with 3274 additions and 1862 deletions

View File

@ -4,7 +4,7 @@
#filter substitution
pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.html");
pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul");
pref("browser.chromeURL", "chrome://browser/content/");
// Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.

View File

@ -11,7 +11,7 @@ window.addEventListener('ContentStart', function() {
let shell = document.getElementById('shell');
// The <browser> element inside it
let browser = document.getElementById('systemapp');
let browser = document.getElementById('homescreen');
// Figure out the native resolution of the screen
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)

View File

@ -1,33 +0,0 @@
<!DOCTYPE html>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml"
id="shell"
windowtype="navigator:browser"
#ifdef ANDROID
sizemode="fullscreen"
#endif
style="background: black; overflow: hidden; width:100%; height:100%; padding: 0px !important"
onunload="shell.stop();">
<head>
<script type="application/javascript;version=1.8"
src="chrome://browser/content/settings.js"> </script>
<script type="application/javascript;version=1.8"
src="chrome://browser/content/shell.js"> </script>
#ifndef MOZ_WIDGET_GONK
<!-- this script handles the screen argument for desktop builds -->
<script type="application/javascript;version=1.8"
src="chrome://browser/content/screen.js"> </script>
<!-- this script handles the "runapp" argument for desktop builds -->
<script type="application/javascript;version=1.8"
src="chrome://browser/content/runapp.js"> </script>
#endif
</head>
<body id="container" style="margin: 0px; width:100%; height:100%;">
<!-- The html:iframe containing the UI is created here. -->
</body>
</html>

View File

@ -6,9 +6,6 @@
Cu.import('resource://gre/modules/ContactService.jsm');
Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
#ifdef MOZ_B2G_FM
Cu.import('resource://gre/modules/DOMFMRadioParent.jsm');
#endif
Cu.import('resource://gre/modules/AlarmService.jsm');
Cu.import('resource://gre/modules/ActivitiesService.jsm');
Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
@ -186,7 +183,7 @@ var shell = {
get contentBrowser() {
delete this.contentBrowser;
return this.contentBrowser = document.getElementById('systemapp');
return this.contentBrowser = document.getElementById('homescreen');
},
get homeURL() {
@ -269,25 +266,25 @@ var shell = {
}
let manifestURL = this.manifestURL;
// <html:iframe id="systemapp"
// <html:iframe id="homescreen"
// mozbrowser="true" allowfullscreen="true"
// style="overflow: hidden; height: 100%; width: 100%; border: none;"
// style="overflow: hidden; -moz-box-flex: 1; border: none;"
// src="data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;'>"/>
let systemAppFrame =
let browserFrame =
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:iframe');
systemAppFrame.setAttribute('id', 'systemapp');
systemAppFrame.setAttribute('mozbrowser', 'true');
systemAppFrame.setAttribute('mozapp', manifestURL);
systemAppFrame.setAttribute('allowfullscreen', 'true');
systemAppFrame.setAttribute('style', "overflow: hidden; height: 100%; width: 100%; border: none;");
systemAppFrame.setAttribute('src', "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;");
document.getElementById('container').appendChild(systemAppFrame);
browserFrame.setAttribute('id', 'homescreen');
browserFrame.setAttribute('mozbrowser', 'true');
browserFrame.setAttribute('mozapp', manifestURL);
browserFrame.setAttribute('allowfullscreen', 'true');
browserFrame.setAttribute('style', "overflow: hidden; -moz-box-flex: 1; border: none;");
browserFrame.setAttribute('src', "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;");
document.getElementById('shell').appendChild(browserFrame);
systemAppFrame.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
.createInstance(Ci.nsISHistory);
browserFrame.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
.createInstance(Ci.nsISHistory);
// Capture all key events so we can filter out hardware buttons
// And send them to Gaia via mozChromeEvents.

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<!-- 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/. -->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="shell"
windowtype="navigator:browser"
#ifdef ANDROID
sizemode="fullscreen"
#endif
style="background: black; overflow: hidden; width:320px; height:480px"
onunload="shell.stop();">
<script type="application/javascript" src="chrome://browser/content/settings.js"/>
<script type="application/javascript" src="chrome://browser/content/shell.js"/>
#ifndef MOZ_WIDGET_GONK
<!-- this script handles the screen argument for desktop builds -->
<script type="application/javascript" src="chrome://browser/content/screen.js"/>
<!-- this script handles the "runapp" argument for desktop builds -->
<script type="application/javascript" src="chrome://browser/content/runapp.js"/>
#endif
<!-- The html:iframe containing the UI is created here. -->
</window>

View File

@ -12,7 +12,7 @@ chrome.jar:
* content/dbg-browser-actors.js (content/dbg-browser-actors.js)
content/forms.js (content/forms.js)
* content/settings.js (content/settings.js)
* content/shell.html (content/shell.html)
* content/shell.xul (content/shell.xul)
* content/shell.js (content/shell.js)
#ifndef ANDROID
content/screen.js (content/screen.js)

View File

@ -1,4 +1,4 @@
{
"revision": "f2d88904536ccd68a3981a7feb17e56b2132838c",
"revision": "752ced5b3cc3208f79806eccf8d8768910f17f2b",
"repo_path": "/integration/gaia-central"
}

View File

@ -172,9 +172,6 @@
@BINPATH@/components/dom_cellbroadcast.xpt
@BINPATH@/components/dom_wappush.xpt
#endif
#ifdef MOZ_B2G_FM
@BINPATH@/components/dom_fm.xpt
#endif
#ifdef MOZ_B2G_BT
@BINPATH@/components/dom_bluetooth.xpt
#endif
@ -475,10 +472,6 @@
@BINPATH@/components/NetworkInterfaceListService.manifest
@BINPATH@/components/NetworkInterfaceListService.js
#endif
#ifdef MOZ_B2G_FM
@BINPATH@/components/DOMFMRadioChild.js
@BINPATH@/components/DOMFMRadio.manifest
#endif
#ifdef MOZ_ENABLE_DBUS
@BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
#endif

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % browserDTD
SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/skin/aboutTabCrashed.css"/>
</head>
<body dir="&locale.dir;">
<div id="error-box">
<p id="main-error-msg">&tabCrashed.header;</p>
<p id="helper-error-msg">&tabCrashed.message;</p>
</div>
<div id="button-box">
<button id="tryAgain">&tabCrashed.tryAgain;</button>
</div>
</body>
<script type="text/javascript;version=1.8"><![CDATA[
function parseQueryString() {
let url = document.documentURI;
let queryString = url.replace(/^about:tabcrashed?e=tabcrashed/, "");
let urlMatch = queryString.match(/u=([^&]+)/);
let url = urlMatch && urlMatch[1] ? decodeURIComponent(urlMatch[1]) : "";
let titleMatch = queryString.match(/d=([^&]*)/);
title = titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "";
return [url, title];
}
let [url, title] = parseQueryString();
document.title = title;
document.getElementById("tryAgain").setAttribute("url", url);
]]></script>
</html>

View File

@ -2341,6 +2341,9 @@ let BrowserOnClick = {
ownerDoc.documentURI.toLowerCase() == "about:newtab") {
this.onE10sAboutNewTab(aEvent, ownerDoc);
}
else if (ownerDoc.documentURI.startsWith("about:tabcrashed")) {
this.onAboutTabCrashed(aEvent, ownerDoc);
}
},
onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
@ -2473,6 +2476,22 @@ let BrowserOnClick = {
}
},
/**
* The about:tabcrashed can't do window.reload() because that
* would reload the page but not use a remote browser.
*/
onAboutTabCrashed: function(aEvent, aOwnerDoc) {
let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
if (!isTopFrame) {
return;
}
let button = aEvent.originalTarget;
if (button.id == "tryAgain") {
openUILinkIn(button.getAttribute("url"), "current");
}
},
ignoreWarningButton: function BrowserOnClick_ignoreWarningButton(aIsMalware) {
// Allow users to override and continue through to the site,
// but add a notify bar as a reminder, so that they don't lose
@ -2582,6 +2601,16 @@ function getWebNavigation()
}
function BrowserReloadWithFlags(reloadFlags) {
let url = gBrowser.currentURI.spec;
if (gBrowser._updateBrowserRemoteness(gBrowser.selectedBrowser,
gBrowser._shouldBrowserBeRemote(url))) {
// If the remoteness has changed, the new browser doesn't have any
// information of what was loaded before, so we need to load the previous
// URL again.
gBrowser.loadURIWithFlags(url, reloadFlags);
return;
}
/* First, we'll try to use the session history object to reload so
* that framesets are handled properly. If we're in a special
* window (such as view-source) that has no session history, fall

View File

@ -1317,7 +1317,7 @@
<![CDATA[
let isRemote = aBrowser.getAttribute("remote") == "true";
if (isRemote == aRemote)
return;
return false;
// Unhook our progress listener.
let tab = this._getTabForBrowser(aBrowser);
@ -1338,6 +1338,8 @@
tab.setAttribute("remote", "true");
else
tab.removeAttribute("remote");
return true;
]]>
</body>
</method>
@ -3118,6 +3120,22 @@
tab.setAttribute("titlechanged", "true");
]]>
</handler>
<handler event="oop-browser-crashed">
<![CDATA[
if (!event.isTrusted)
return;
let browser = event.originalTarget;
let title = browser.contentTitle;
let uri = browser.currentURI;
this._updateBrowserRemoteness(browser, false);
browser.setAttribute("crashedPageTitle", title);
browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
browser.removeAttribute("crashedPageTitle");
]]>
</handler>
</handlers>
</binding>

View File

@ -55,6 +55,7 @@ browser.jar:
content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png)
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)
content/browser/aboutSocialError.xhtml (content/aboutSocialError.xhtml)
content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml)
* content/browser/browser.css (content/browser.css)
* content/browser/browser.js (content/browser.js)
* content/browser/browser.xul (content/browser.xul)

View File

@ -44,6 +44,10 @@ static RedirEntry kRedirMap[] = {
{ "socialerror", "chrome://browser/content/aboutSocialError.xhtml",
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
{ "tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
{ "feeds", "chrome://browser/content/feeds/subscribe.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |

View File

@ -90,6 +90,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
#endif
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "certerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "socialerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "tabcrashed", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "feeds", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },

View File

@ -668,6 +668,10 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY pluginActivateAlways.label "Allow and Remember">
<!ENTITY pluginBlockNow.label "Block Plugin">
<!ENTITY tabCrashed.header "Tab crashed">
<!ENTITY tabCrashed.message "Well, this is embarrassing. We tried to display this Web page, but it's not responding.">
<!ENTITY tabCrashed.tryAgain "Try Again">
<!-- LOCALIZATION NOTE: the following strings are unused in Australis, they're
kept here to avoid warnings from l10n tools like compare-locales on
l10n-central. They will be definitely removed when Australis is ready

View File

@ -0,0 +1,98 @@
body {
background-color: rgb(241, 244, 248);
margin-top: 2em;
font: message-box;
font-size: 100%;
}
p {
font-size: .8em;
}
#error-box {
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-moz-padding-start: 30px;
}
#error-box:-moz-locale-dir(rtl) {
background-position: right 4px;
}
#main-error-msg {
color: #4b4b4b;
font-weight: bold;
}
#button-box {
text-align: center;
width: 75%;
margin: 0 auto;
}
@media all and (min-width: 300px) {
#error-box {
max-width: 50%;
margin: 0 auto;
background-image: url('chrome://global/skin/icons/information-32.png');
min-height: 36px;
-moz-padding-start: 38px;
}
button {
width: auto !important;
min-width: 150px;
}
}
@media all and (min-width: 780px) {
#error-box {
max-width: 30%;
}
}
button {
font: message-box;
font-size: 0.6875em;
-moz-appearance: none;
-moz-user-select: none;
width: 100%;
margin: 2px 0;
padding: 2px 6px;
line-height: 1.2;
background-color: hsla(210,30%,95%,.1);
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-clip: padding-box;
border: 1px solid hsla(210,15%,25%,.4);
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
border-radius: 3px;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover {
background-color: hsla(210,30%,95%,.8);
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1),
0 0 3px hsla(210,15%,25%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover:active {
background-color: hsla(210,15%,25%,.2);
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
0 0 2px hsla(210,15%,25%,.4) inset;
transition-property: background-color, border-color, box-shadow;
transition-duration: 10ms;
transition-timing-function: linear;
}

View File

@ -18,6 +18,7 @@ browser.jar:
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/aboutSyncTabs.css
#endif
skin/classic/browser/aboutTabCrashed.css
skin/classic/browser/actionicon-tab.png
* skin/classic/browser/browser.css
skin/classic/browser/click-to-play-warning-stripes.png

View File

@ -0,0 +1,98 @@
body {
background-color: rgb(241, 244, 248);
margin-top: 2em;
font: message-box;
font-size: 100%;
}
p {
font-size: .8em;
}
#error-box {
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-moz-padding-start: 30px;
}
#error-box:-moz-locale-dir(rtl) {
background-position: right 4px;
}
#main-error-msg {
color: #4b4b4b;
font-weight: bold;
}
#button-box {
text-align: center;
width: 75%;
margin: 0 auto;
}
@media all and (min-width: 300px) {
#error-box {
max-width: 50%;
margin: 0 auto;
background-image: url('chrome://global/skin/icons/information-32.png');
min-height: 36px;
-moz-padding-start: 38px;
}
button {
width: auto !important;
min-width: 150px;
}
}
@media all and (min-width: 780px) {
#error-box {
max-width: 30%;
}
}
button {
font: message-box;
font-size: 0.6875em;
-moz-appearance: none;
-moz-user-select: none;
width: 100%;
margin: 2px 0;
padding: 2px 6px;
line-height: 1.2;
background-color: hsla(210,30%,95%,.1);
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-clip: padding-box;
border: 1px solid hsla(210,15%,25%,.4);
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
border-radius: 3px;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover {
background-color: hsla(210,30%,95%,.8);
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1),
0 0 3px hsla(210,15%,25%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover:active {
background-color: hsla(210,15%,25%,.2);
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
0 0 2px hsla(210,15%,25%,.4) inset;
transition-property: background-color, border-color, box-shadow;
transition-duration: 10ms;
transition-timing-function: linear;
}

View File

@ -17,6 +17,7 @@ browser.jar:
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/aboutSyncTabs.css
#endif
skin/classic/browser/aboutTabCrashed.css
skin/classic/browser/actionicon-tab.png
skin/classic/browser/actionicon-tab@2x.png
* skin/classic/browser/browser.css (browser.css)

View File

@ -0,0 +1,98 @@
body {
background-color: rgb(241, 244, 248);
margin-top: 2em;
font: message-box;
font-size: 100%;
}
p {
font-size: .8em;
}
#error-box {
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-moz-padding-start: 30px;
}
#error-box:-moz-locale-dir(rtl) {
background-position: right 4px;
}
#main-error-msg {
color: #4b4b4b;
font-weight: bold;
}
#button-box {
text-align: center;
width: 75%;
margin: 0 auto;
}
@media all and (min-width: 300px) {
#error-box {
max-width: 50%;
margin: 0 auto;
background-image: url('chrome://global/skin/icons/information-32.png');
min-height: 36px;
-moz-padding-start: 38px;
}
button {
width: auto !important;
min-width: 150px;
}
}
@media all and (min-width: 780px) {
#error-box {
max-width: 30%;
}
}
button {
font: message-box;
font-size: 0.6875em;
-moz-appearance: none;
-moz-user-select: none;
width: 100%;
margin: 2px 0;
padding: 2px 6px;
line-height: 1.2;
background-color: hsla(210,30%,95%,.1);
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-clip: padding-box;
border: 1px solid hsla(210,15%,25%,.4);
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
border-radius: 3px;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover {
background-color: hsla(210,30%,95%,.8);
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1),
0 0 3px hsla(210,15%,25%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover:active {
background-color: hsla(210,15%,25%,.2);
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
0 0 2px hsla(210,15%,25%,.4) inset;
transition-property: background-color, border-color, box-shadow;
transition-duration: 10ms;
transition-timing-function: linear;
}

View File

@ -20,6 +20,7 @@ browser.jar:
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/aboutSyncTabs.css
#endif
skin/classic/browser/aboutTabCrashed.css
skin/classic/browser/actionicon-tab.png
skin/classic/browser/appmenu-icons.png
skin/classic/browser/appmenu-dropmarker.png
@ -282,6 +283,7 @@ browser.jar:
#ifdef MOZ_SERVICES_SYNC
skin/classic/aero/browser/aboutSyncTabs.css
#endif
skin/classic/aero/browser/aboutTabCrashed.css
skin/classic/aero/browser/actionicon-tab.png
skin/classic/aero/browser/appmenu-dropmarker.png
skin/classic/aero/browser/appmenu-icons.png

View File

@ -25,6 +25,7 @@
#include "mozilla/TimeStamp.h"
#include "nsContentListDeclarations.h"
#include "nsMathUtils.h"
#include "Units.h"
class imgICache;
class imgIContainer;
@ -1508,8 +1509,7 @@ public:
* will return viewport information that specifies default information.
*/
static nsViewportInfo GetViewportInfo(nsIDocument* aDocument,
uint32_t aDisplayWidth,
uint32_t aDisplayHeight);
const mozilla::ScreenIntSize& aDisplaySize);
// Call EnterMicroTask when you're entering JS execution.
// Usually the best way to do this is to use nsAutoMicroTask.

View File

@ -26,6 +26,7 @@
#include "nsPropertyTable.h" // for member
#include "nsTHashtable.h" // for member
#include "mozilla/dom/DocumentBinding.h"
#include "Units.h"
class imgIRequest;
class nsAString;
@ -622,8 +623,7 @@ public:
*/
Element* GetRootElement() const;
virtual nsViewportInfo GetViewportInfo(uint32_t aDisplayWidth,
uint32_t aDisplayHeight) = 0;
virtual nsViewportInfo GetViewportInfo(const mozilla::ScreenIntSize& aDisplaySize) = 0;
/**
* True iff this doc will ignore manual character encoding overrides.

View File

@ -7,16 +7,15 @@
#include <stdint.h>
#include "nscore.h"
#include "Units.h"
/**
* Default values for the nsViewportInfo class.
*/
static const double kViewportMinScale = 0.0;
static const double kViewportMaxScale = 10.0;
static const uint32_t kViewportMinWidth = 200;
static const uint32_t kViewportMaxWidth = 10000;
static const uint32_t kViewportMinHeight = 223;
static const uint32_t kViewportMaxHeight = 10000;
static const mozilla::LayoutDeviceToScreenScale kViewportMinScale(0.0f);
static const mozilla::LayoutDeviceToScreenScale kViewportMaxScale(10.0f);
static const mozilla::CSSIntSize kViewportMinSize(200, 223);
static const mozilla::CSSIntSize kViewportMaxSize(10000, 10000);
static const int32_t kViewportDefaultScreenWidth = 980;
/**
@ -26,43 +25,40 @@ static const int32_t kViewportDefaultScreenWidth = 980;
class MOZ_STACK_CLASS nsViewportInfo
{
public:
nsViewportInfo(uint32_t aDisplayWidth, uint32_t aDisplayHeight) :
nsViewportInfo(const mozilla::ScreenIntSize& aDisplaySize) :
mDefaultZoom(1.0),
mMinZoom(kViewportMinScale),
mMaxZoom(kViewportMaxScale),
mWidth(aDisplayWidth),
mHeight(aDisplayHeight),
mAutoSize(true),
mAllowZoom(true)
{
mSize = mozilla::gfx::RoundedToInt(mozilla::ScreenSize(aDisplaySize) / mDefaultZoom);
mozilla::CSSToLayoutDeviceScale pixelRatio(1.0f);
mMinZoom = pixelRatio * kViewportMinScale;
mMaxZoom = pixelRatio * kViewportMaxScale;
ConstrainViewportValues();
}
nsViewportInfo(double aDefaultZoom,
double aMinZoom,
double aMaxZoom,
uint32_t aWidth,
uint32_t aHeight,
nsViewportInfo(const mozilla::CSSToScreenScale& aDefaultZoom,
const mozilla::CSSToScreenScale& aMinZoom,
const mozilla::CSSToScreenScale& aMaxZoom,
const mozilla::CSSIntSize& aSize,
bool aAutoSize,
bool aAllowZoom) :
mDefaultZoom(aDefaultZoom),
mMinZoom(aMinZoom),
mMaxZoom(aMaxZoom),
mWidth(aWidth),
mHeight(aHeight),
mSize(aSize),
mAutoSize(aAutoSize),
mAllowZoom(aAllowZoom)
{
ConstrainViewportValues();
}
double GetDefaultZoom() { return mDefaultZoom; }
void SetDefaultZoom(const double aDefaultZoom);
double GetMinZoom() { return mMinZoom; }
double GetMaxZoom() { return mMaxZoom; }
mozilla::CSSToScreenScale GetDefaultZoom() { return mDefaultZoom; }
void SetDefaultZoom(const mozilla::CSSToScreenScale& aDefaultZoom);
mozilla::CSSToScreenScale GetMinZoom() { return mMinZoom; }
mozilla::CSSToScreenScale GetMaxZoom() { return mMaxZoom; }
uint32_t GetWidth() { return mWidth; }
uint32_t GetHeight() { return mHeight; }
mozilla::CSSIntSize GetSize() { return mSize; }
bool IsAutoSizeEnabled() { return mAutoSize; }
bool IsZoomAllowed() { return mAllowZoom; }
@ -78,21 +74,16 @@ class MOZ_STACK_CLASS nsViewportInfo
// Default zoom indicates the level at which the display is 'zoomed in'
// initially for the user, upon loading of the page.
double mDefaultZoom;
mozilla::CSSToScreenScale mDefaultZoom;
// The minimum zoom level permitted by the page.
double mMinZoom;
mozilla::CSSToScreenScale mMinZoom;
// The maximum zoom level permitted by the page.
double mMaxZoom;
mozilla::CSSToScreenScale mMaxZoom;
// The width of the viewport, specified by the <meta name="viewport"> tag,
// in CSS pixels.
uint32_t mWidth;
// The height of the viewport, specified by the <meta name="viewport"> tag,
// in CSS pixels.
uint32_t mHeight;
// The size of the viewport, specified by the <meta name="viewport"> tag.
mozilla::CSSIntSize mSize;
// Whether or not we should automatically size the viewport to the device's
// width. This is true if the document has been optimized for mobile, and

View File

@ -4875,10 +4875,9 @@ static void ProcessViewportToken(nsIDocument *aDocument,
/* static */
nsViewportInfo
nsContentUtils::GetViewportInfo(nsIDocument *aDocument,
uint32_t aDisplayWidth,
uint32_t aDisplayHeight)
const ScreenIntSize& aDisplaySize)
{
return aDocument->GetViewportInfo(aDisplayWidth, aDisplayHeight);
return aDocument->GetViewportInfo(aDisplaySize);
}
/* static */

View File

@ -6782,12 +6782,11 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
}
nsViewportInfo
nsDocument::GetViewportInfo(uint32_t aDisplayWidth,
uint32_t aDisplayHeight)
nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
{
switch (mViewportType) {
case DisplayWidthHeight:
return nsViewportInfo(aDisplayWidth, aDisplayHeight);
return nsViewportInfo(aDisplaySize);
case Unknown:
{
nsAutoString viewport;
@ -6807,8 +6806,7 @@ nsDocument::GetViewportInfo(uint32_t aDisplayWidth,
{
// We're making an assumption that the docType can't change here
mViewportType = DisplayWidthHeight;
nsViewportInfo ret(aDisplayWidth, aDisplayHeight);
return ret;
return nsViewportInfo(aDisplaySize);
}
}
}
@ -6817,8 +6815,7 @@ nsDocument::GetViewportInfo(uint32_t aDisplayWidth,
GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
if (handheldFriendly.EqualsLiteral("true")) {
mViewportType = DisplayWidthHeight;
nsViewportInfo ret(aDisplayWidth, aDisplayHeight);
return ret;
return nsViewportInfo(aDisplaySize);
}
}
@ -6826,14 +6823,14 @@ nsDocument::GetViewportInfo(uint32_t aDisplayWidth,
GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
nsresult errorCode;
mScaleMinFloat = minScaleStr.ToFloat(&errorCode);
mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
if (NS_FAILED(errorCode)) {
mScaleMinFloat = kViewportMinScale;
}
mScaleMinFloat = std::min((double)mScaleMinFloat, kViewportMaxScale);
mScaleMinFloat = std::max((double)mScaleMinFloat, kViewportMinScale);
mScaleMinFloat = mozilla::clamped(
mScaleMinFloat, kViewportMinScale, kViewportMaxScale);
nsAutoString maxScaleStr;
GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
@ -6841,20 +6838,20 @@ nsDocument::GetViewportInfo(uint32_t aDisplayWidth,
// We define a special error code variable for the scale and max scale,
// because they are used later (see the width calculations).
nsresult scaleMaxErrorCode;
mScaleMaxFloat = maxScaleStr.ToFloat(&scaleMaxErrorCode);
mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
if (NS_FAILED(scaleMaxErrorCode)) {
mScaleMaxFloat = kViewportMaxScale;
}
mScaleMaxFloat = std::min((double)mScaleMaxFloat, kViewportMaxScale);
mScaleMaxFloat = std::max((double)mScaleMaxFloat, kViewportMinScale);
mScaleMaxFloat = mozilla::clamped(
mScaleMaxFloat, kViewportMinScale, kViewportMaxScale);
nsAutoString scaleStr;
GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
nsresult scaleErrorCode;
mScaleFloat = scaleStr.ToFloat(&scaleErrorCode);
mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
nsAutoString widthStr, heightStr;
@ -6869,20 +6866,19 @@ nsDocument::GetViewportInfo(uint32_t aDisplayWidth,
if (widthStr.IsEmpty() &&
(heightStr.EqualsLiteral("device-height") ||
(mScaleFloat /* not adjusted for pixel ratio */ == 1.0)))
(mScaleFloat.scale == 1.0)))
{
mAutoSize = true;
}
nsresult widthErrorCode, heightErrorCode;
mViewportWidth = widthStr.ToInteger(&widthErrorCode);
mViewportHeight = heightStr.ToInteger(&heightErrorCode);
mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
// If width or height has not been set to a valid number by this point,
// fall back to a default value.
mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportWidth > 0);
mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportHeight > 0);
mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0);
mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0);
mAllowZoom = true;
nsAutoString userScalable;
@ -6903,63 +6899,62 @@ nsDocument::GetViewportInfo(uint32_t aDisplayWidth,
}
case Specified:
default:
uint32_t width = mViewportWidth, height = mViewportHeight;
CSSIntSize size = mViewportSize;
if (!mValidWidth) {
if (mValidHeight && aDisplayWidth > 0 && aDisplayHeight > 0) {
width = uint32_t((height * aDisplayWidth) / aDisplayHeight);
if (mValidHeight && !aDisplaySize.IsEmpty()) {
size.width = int32_t(size.height * aDisplaySize.width / aDisplaySize.height);
} else {
width = Preferences::GetInt("browser.viewport.desktopWidth",
kViewportDefaultScreenWidth);
size.width = Preferences::GetInt("browser.viewport.desktopWidth",
kViewportDefaultScreenWidth);
}
}
if (!mValidHeight) {
if (aDisplayWidth > 0 && aDisplayHeight > 0) {
height = uint32_t((width * aDisplayHeight) / aDisplayWidth);
if (!aDisplaySize.IsEmpty()) {
size.height = int32_t(size.width * aDisplaySize.height / aDisplaySize.width);
} else {
height = width;
size.height = size.width;
}
}
// Now convert the scale into device pixels per CSS pixel.
nsIWidget *widget = nsContentUtils::WidgetForDocument(this);
double pixelRatio = widget ? widget->GetDefaultScale() : 1.0;
float scaleFloat = mScaleFloat * pixelRatio;
float scaleMinFloat= mScaleMinFloat * pixelRatio;
float scaleMaxFloat = mScaleMaxFloat * pixelRatio;
CSSToLayoutDeviceScale pixelRatio(widget ? widget->GetDefaultScale() : 1.0f);
CSSToScreenScale scaleFloat = mScaleFloat * pixelRatio;
CSSToScreenScale scaleMinFloat = mScaleMinFloat * pixelRatio;
CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * pixelRatio;
if (mAutoSize) {
// aDisplayWidth and aDisplayHeight are in device pixels; convert them to
// CSS pixels for the viewport size.
width = aDisplayWidth / pixelRatio;
height = aDisplayHeight / pixelRatio;
// aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size.
CSSToScreenScale defaultPixelScale = pixelRatio * LayoutDeviceToScreenScale(1.0f);
size = mozilla::gfx::RoundedToInt(ScreenSize(aDisplaySize) / defaultPixelScale);
}
width = std::min(width, kViewportMaxWidth);
width = std::max(width, kViewportMinWidth);
size.width = clamped(size.width, kViewportMinSize.width, kViewportMaxSize.width);
// Also recalculate the default zoom, if it wasn't specified in the metadata,
// and the width is specified.
if (mScaleStrEmpty && !mWidthStrEmpty) {
scaleFloat = std::max(scaleFloat, float(aDisplayWidth) / float(width));
CSSToScreenScale defaultScale(float(aDisplaySize.width) / float(size.width));
scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
}
height = std::min(height, kViewportMaxHeight);
height = std::max(height, kViewportMinHeight);
size.height = clamped(size.height, kViewportMinSize.height, kViewportMaxSize.height);
// We need to perform a conversion, but only if the initial or maximum
// scale were set explicitly by the user.
if (mValidScaleFloat) {
width = std::max(width, (uint32_t)(aDisplayWidth / scaleFloat));
height = std::max(height, (uint32_t)(aDisplayHeight / scaleFloat));
CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleFloat);
size.width = std::max(size.width, displaySize.width);
size.height = std::max(size.height, displaySize.height);
} else if (mValidMaxScale) {
width = std::max(width, (uint32_t)(aDisplayWidth / scaleMaxFloat));
height = std::max(height, (uint32_t)(aDisplayHeight / scaleMaxFloat));
CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleMaxFloat);
size.width = std::max(size.width, displaySize.width);
size.height = std::max(size.height, displaySize.height);
}
nsViewportInfo ret(scaleFloat, scaleMinFloat, scaleMaxFloat, width, height,
mAutoSize, mAllowZoom);
return ret;
return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
mAutoSize, mAllowZoom);
}
}

View File

@ -753,9 +753,7 @@ public:
nsRadioGroupStruct* GetRadioGroup(const nsAString& aName) const;
nsRadioGroupStruct* GetOrCreateRadioGroup(const nsAString& aName);
virtual nsViewportInfo GetViewportInfo(uint32_t aDisplayWidth,
uint32_t aDisplayHeight) MOZ_OVERRIDE;
virtual nsViewportInfo GetViewportInfo(const mozilla::ScreenIntSize& aDisplaySize) MOZ_OVERRIDE;
private:
nsRadioGroupStruct* GetRadioGroupInternal(const nsAString& aName) const;
@ -1412,9 +1410,12 @@ private:
// These member variables cache information about the viewport so we don't have to
// recalculate it each time.
bool mValidWidth, mValidHeight;
float mScaleMinFloat, mScaleMaxFloat, mScaleFloat, mPixelRatio;
mozilla::LayoutDeviceToScreenScale mScaleMinFloat;
mozilla::LayoutDeviceToScreenScale mScaleMaxFloat;
mozilla::LayoutDeviceToScreenScale mScaleFloat;
mozilla::CSSToLayoutDeviceScale mPixelRatio;
bool mAutoSize, mAllowZoom, mValidScaleFloat, mValidMaxScale, mScaleStrEmpty, mWidthStrEmpty;
uint32_t mViewportWidth, mViewportHeight;
mozilla::CSSIntSize mViewportSize;
nsrefcnt mStackRefCnt;
bool mNeedsReleaseAfterStackRefCntRelease;

View File

@ -638,6 +638,7 @@ GK_ATOM(onalerting, "onalerting")
GK_ATOM(onanimationend, "onanimationend")
GK_ATOM(onanimationiteration, "onanimationiteration")
GK_ATOM(onanimationstart, "onanimationstart")
GK_ATOM(onantennaavailablechange, "onantennaavailablechange")
GK_ATOM(onAppCommand, "onAppCommand")
GK_ATOM(onaudioprocess, "onaudioprocess")
GK_ATOM(onbeforecopy, "onbeforecopy")
@ -709,6 +710,7 @@ GK_ATOM(onemergencycbmodechange, "onemergencycbmodechange")
GK_ATOM(onerror, "onerror")
GK_ATOM(onfailed, "onfailed")
GK_ATOM(onfocus, "onfocus")
GK_ATOM(onfrequencychange, "onfrequencychange")
GK_ATOM(onget, "onget")
GK_ATOM(ongroupchange, "ongroupchange")
GK_ATOM(onhashchange, "onhashchange")
@ -771,6 +773,7 @@ GK_ATOM(onremoteheld, "onremoteheld")
GK_ATOM(onremoteresumed, "onremoteresumed")
GK_ATOM(onretrieving, "onretrieving")
GK_ATOM(onRequest, "onRequest")
GK_ATOM(onrequestmediaplaystatus, "onrequestmediaplaystatus")
GK_ATOM(onreset, "onreset")
GK_ATOM(onresuming, "onresuming")
GK_ATOM(onMozBeforeResize, "onMozBeforeResize")
@ -785,8 +788,6 @@ GK_ATOM(onshow, "onshow")
GK_ATOM(onstatechange, "onstatechange")
GK_ATOM(onstatuschanged, "onstatuschanged")
GK_ATOM(onstkcommand, "onstkcommand")
GK_ATOM(onantennastatechange, "onantennastatechange")
GK_ATOM(onseekcomplete, "onseekcomplete")
GK_ATOM(onstksessionend, "onstksessionend")
GK_ATOM(onsubmit, "onsubmit")
GK_ATOM(onsuccess, "onsuccess")

View File

@ -6,10 +6,12 @@
#include "mozilla/Assertions.h"
#include <algorithm>
using namespace mozilla;
void
nsViewportInfo::SetDefaultZoom(const double aDefaultZoom)
nsViewportInfo::SetDefaultZoom(const CSSToScreenScale& aDefaultZoom)
{
MOZ_ASSERT(aDefaultZoom >= 0.0f);
MOZ_ASSERT(aDefaultZoom.scale >= 0.0f);
mDefaultZoom = aDefaultZoom;
}
@ -20,6 +22,6 @@ nsViewportInfo::ConstrainViewportValues()
// dev.w3.org/csswg/css-device-adapt section 6.2
mMaxZoom = std::max(mMinZoom, mMaxZoom);
mDefaultZoom = std::min(mDefaultZoom, mMaxZoom);
mDefaultZoom = std::max(mDefaultZoom, mMinZoom);
mDefaultZoom = mDefaultZoom < mMaxZoom ? mDefaultZoom : mMaxZoom;
mDefaultZoom = mDefaultZoom > mMinZoom ? mDefaultZoom : mMinZoom;
}

View File

@ -3962,7 +3962,7 @@ bool
nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog)
{
if (mIsPrintingOrPP && aDisplayErrorDialog) {
DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr);
DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
}
return mIsPrintingOrPP;
@ -4118,7 +4118,7 @@ nsDocShell::LoadURI(const PRUnichar * aURI,
// what happens
if (NS_ERROR_MALFORMED_URI == rv) {
DisplayLoadError(rv, uri, aURI);
DisplayLoadError(rv, uri, aURI, nullptr);
}
if (NS_FAILED(rv) || !uri)
@ -4333,6 +4333,15 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
bucketId);
cssClass.AssignLiteral("blacklist");
} else if (NS_ERROR_CONTENT_CRASHED == aError) {
errorPage.AssignLiteral("tabcrashed");
error.AssignLiteral("tabcrashed");
nsCOMPtr<EventTarget> handler = mChromeEventHandler;
if (handler) {
nsCOMPtr<Element> element = do_QueryInterface(handler);
element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
}
}
else {
// Errors requiring simple formatting

View File

@ -509,9 +509,6 @@ protected:
nsresult EnsureTransferableHookData();
NS_IMETHOD EnsureFind();
nsresult RefreshURIFromQueue();
NS_IMETHOD DisplayLoadError(nsresult aError, nsIURI *aURI,
const PRUnichar *aURL,
nsIChannel* aFailedChannel = nullptr);
NS_IMETHOD LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
const char *aErrorPage,
const PRUnichar *aErrorType,

View File

@ -42,7 +42,7 @@ interface nsIVariant;
interface nsIPrivacyTransitionObserver;
interface nsIReflowObserver;
[scriptable, builtinclass, uuid(4bb2261b-4c13-44a4-ace3-fc2eec17cc34)]
[scriptable, builtinclass, uuid(62f1b40d-1d15-4640-95dc-20caae775bd1)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
@ -411,6 +411,22 @@ interface nsIDocShell : nsIDocShellTreeItem
/* attribute to access whether error pages are enabled */
attribute boolean useErrorPages;
/**
* Display a load error in a frame while keeping that frame's currentURI
* pointing correctly to the page where the error ocurred, rather than to
* the error document page. You must provide either the aURI or aURL parameter.
*
* @param aError The error code to be displayed
* @param aURI nsIURI of the page where the error happened
* @param aURL wstring of the page where the error happened
* @param aFailedChannel The channel related to this error
*/
void displayLoadError(in nsresult aError,
in nsIURI aURI,
in wstring aURL,
[optional] in nsIChannel aFailedChannel);
/**
* Keeps track of the previous SHTransaction index and the current
* SHTransaction index at the time that the doc shell begins to load.

View File

@ -65,6 +65,10 @@
#include "AudioChannelManager.h"
#endif
#ifdef MOZ_B2G_FM
#include "mozilla/dom/FMRadio.h"
#endif
#include "nsIDOMGlobalPropertyInitializer.h"
#include "nsJSUtils.h"
@ -195,6 +199,13 @@ Navigator::Invalidate()
mBatteryManager = nullptr;
}
#ifdef MOZ_B2G_FM
if (mFMRadio) {
mFMRadio->Shutdown();
mFMRadio = nullptr;
}
#endif
if (mPowerManager) {
mPowerManager->Shutdown();
mPowerManager = nullptr;
@ -1044,6 +1055,34 @@ Navigator::GetMozNotification(ErrorResult& aRv)
return mNotification;
}
#ifdef MOZ_B2G_FM
using mozilla::dom::FMRadio;
FMRadio*
Navigator::GetMozFMRadio(ErrorResult& aRv)
{
if (!mFMRadio) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
mFMRadio = new FMRadio();
mFMRadio->Init(mWindow);
}
return mFMRadio;
}
#endif // MOZ_B2G_FM
//*****************************************************************************
// Navigator::nsINavigatorBattery
//*****************************************************************************
battery::BatteryManager*
Navigator::GetBattery(ErrorResult& aRv)
{
@ -1710,6 +1749,16 @@ Navigator::HasBluetoothSupport(JSContext* /* unused */, JSObject* aGlobal)
}
#endif // MOZ_B2G_BT
#ifdef MOZ_B2G_FM
/* static */
bool
Navigator::HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "fmradio");
}
#endif // MOZ_B2G_FM
#ifdef MOZ_TIME_MANAGER
/* static */
bool

View File

@ -51,6 +51,10 @@ namespace battery {
class BatteryManager;
} // namespace battery
#ifdef MOZ_B2G_FM
class FMRadio;
#endif
class DesktopNotificationCenter;
class MobileMessageManager;
class MozIdleObserver;
@ -230,6 +234,9 @@ public:
#ifdef MOZ_GAMEPAD
void GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
#endif // MOZ_GAMEPAD
#ifdef MOZ_B2G_FM
FMRadio* GetMozFMRadio(ErrorResult& aRv);
#endif
#ifdef MOZ_B2G_BT
bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
#endif // MOZ_B2G_BT
@ -283,6 +290,9 @@ public:
#ifdef MOZ_B2G_BT
static bool HasBluetoothSupport(JSContext* /* unused */, JSObject* aGlobal);
#endif // MOZ_B2G_BT
#ifdef MOZ_B2G_FM
static bool HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal);
#endif // MOZ_B2G_FM
#ifdef MOZ_TIME_MANAGER
static bool HasTimeSupport(JSContext* /* unused */, JSObject* aGlobal);
#endif // MOZ_TIME_MANAGER
@ -314,6 +324,9 @@ private:
nsRefPtr<Geolocation> mGeolocation;
nsRefPtr<DesktopNotificationCenter> mNotification;
nsRefPtr<battery::BatteryManager> mBatteryManager;
#ifdef MOZ_B2G_FM
nsRefPtr<FMRadio> mFMRadio;
#endif
nsRefPtr<power::PowerManager> mPowerManager;
nsRefPtr<MobileMessageManager> mMobileMessageManager;
#ifdef MOZ_B2G_RIL

View File

@ -537,11 +537,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
DOM_DEFAULT_SCRIPTABLE_FLAGS)
#endif
#ifdef MOZ_B2G_FM
NS_DEFINE_CLASSINFO_DATA(FMRadio, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
#endif
#ifdef MOZ_B2G_BT
NS_DEFINE_CLASSINFO_DATA(BluetoothDevice, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
@ -1399,13 +1394,6 @@ nsDOMClassInfo::Init()
#endif
#ifdef MOZ_B2G_FM
DOM_CLASSINFO_MAP_BEGIN(FMRadio, nsIFMRadio)
DOM_CLASSINFO_MAP_ENTRY(nsIFMRadio)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
#endif
#ifdef MOZ_B2G_BT
DOM_CLASSINFO_MAP_BEGIN(BluetoothDevice, nsIDOMBluetoothDevice)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothDevice)

View File

@ -130,10 +130,6 @@ DOMCI_CLASS(MediaQueryList)
DOMCI_CLASS(MozIccManager)
#endif
#ifdef MOZ_B2G_FM
DOMCI_CLASS(FMRadio)
#endif
#ifdef MOZ_B2G_BT
DOMCI_CLASS(BluetoothDevice)
#endif

View File

@ -281,13 +281,13 @@ nsDOMWindowUtils::GetViewportInfo(uint32_t aDisplayWidth,
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
NS_ENSURE_STATE(doc);
nsViewportInfo info = nsContentUtils::GetViewportInfo(doc, aDisplayWidth, aDisplayHeight);
*aDefaultZoom = info.GetDefaultZoom();
nsViewportInfo info = nsContentUtils::GetViewportInfo(doc, ScreenIntSize(aDisplayWidth, aDisplayHeight));
*aDefaultZoom = info.GetDefaultZoom().scale;
*aAllowZoom = info.IsZoomAllowed();
*aMinZoom = info.GetMinZoom();
*aMaxZoom = info.GetMaxZoom();
*aWidth = info.GetWidth();
*aHeight = info.GetHeight();
*aMinZoom = info.GetMinZoom().scale;
*aMaxZoom = info.GetMaxZoom().scale;
*aWidth = info.GetSize().width;
*aHeight = info.GetSize().height;
*aAutoSize = info.IsAutoSizeEnabled();
return NS_OK;
}

View File

@ -345,6 +345,15 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData)
e->InitBluetoothStatusChangedEvent(aData.name(), false, false,
address, status);
DispatchTrustedEvent(event);
} else if (aData.name().EqualsLiteral(REQUEST_MEDIA_PLAYSTATUS_ID)) {
nsCOMPtr<nsIDOMEvent> event;
nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
NS_ENSURE_SUCCESS_VOID(rv);
rv = event->InitEvent(aData.name(), false, false);
NS_ENSURE_SUCCESS_VOID(rv);
DispatchTrustedEvent(event);
} else {
#ifdef DEBUG
nsCString warningMsg;

View File

@ -144,6 +144,7 @@ public:
IMPL_EVENT_HANDLER(a2dpstatuschanged);
IMPL_EVENT_HANDLER(hfpstatuschanged);
IMPL_EVENT_HANDLER(pairedstatuschanged);
IMPL_EVENT_HANDLER(requestmediaplaystatus);
IMPL_EVENT_HANDLER(scostatuschanged);
nsPIDOMWindow* GetParentObject() const

View File

@ -62,18 +62,24 @@ extern bool gBluetoothDebugFlag;
/**
* When the connection status of a Bluetooth profile is changed, we'll
* distribute one of the following events.
* dispatch one of the following events.
*/
#define A2DP_STATUS_CHANGED_ID "a2dpstatuschanged"
#define HFP_STATUS_CHANGED_ID "hfpstatuschanged"
#define SCO_STATUS_CHANGED_ID "scostatuschanged"
/**
* When the pair status of a Bluetooth device is changed, we'll distribute an
* When the pair status of a Bluetooth device is changed, we'll dispatch an
* event.
*/
#define PAIRED_STATUS_CHANGED_ID "pairedstatuschanged"
/**
* When receiving a query about current play status from remote device, we'll
* dispatch an event.
*/
#define REQUEST_MEDIA_PLAYSTATUS_ID "requestmediaplaystatus"
// Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx)
#define BLUETOOTH_ADDRESS_LENGTH 17
#define BLUETOOTH_ADDRESS_NONE "00:00:00:00:00:00"

View File

@ -178,11 +178,21 @@ static nsTArray<uint32_t> sAuthorizedServiceClass;
static nsString sAdapterPath;
static Atomic<int32_t> sIsPairing(0);
static int sConnectedDeviceCount = 0;
static Monitor sStopBluetoothMonitor("BluetoothService.sStopBluetoothMonitor");
static StaticAutoPtr<Monitor> sStopBluetoothMonitor;
typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&);
typedef bool (*FilterFunc)(const BluetoothValue&);
BluetoothDBusService::BluetoothDBusService()
{
sStopBluetoothMonitor = new Monitor("BluetoothService.sStopBluetoothMonitor");
}
BluetoothDBusService::~BluetoothDBusService()
{
sStopBluetoothMonitor = nullptr;
}
static bool
GetConnectedDevicesFilter(const BluetoothValue& aValue)
{
@ -1298,10 +1308,10 @@ private:
nsString mAdapterPath;
};
class SendPlayStatusTask : public nsRunnable
class RequestPlayStatusTask : public nsRunnable
{
public:
SendPlayStatusTask()
RequestPlayStatusTask()
{
MOZ_ASSERT(!NS_IsMainThread());
}
@ -1310,15 +1320,14 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE);
BluetoothSignal signal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID),
NS_LITERAL_STRING(KEY_ADAPTER),
InfallibleTArray<BluetoothNamedValue>());
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
bs->DistributeSignal(signal);
bs->UpdatePlayStatus(a2dp->GetDuration(),
a2dp->GetPosition(),
a2dp->GetPlayStatus());
return NS_OK;
}
};
@ -1494,15 +1503,13 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
signal.value() = parameters;
NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal));
} else if (property.name().EqualsLiteral("Connected")) {
MonitorAutoLock lock(sStopBluetoothMonitor);
MonitorAutoLock lock(*sStopBluetoothMonitor);
if (property.value().get_bool()) {
++sConnectedDeviceCount;
} else {
MOZ_ASSERT(sConnectedDeviceCount > 0);
--sConnectedDeviceCount;
if (sConnectedDeviceCount == 0) {
if (--sConnectedDeviceCount == 0) {
lock.Notify();
}
}
@ -1533,7 +1540,7 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
sSinkProperties,
ArrayLength(sSinkProperties));
} else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "GetPlayStatus")) {
NS_DispatchToMainThread(new SendPlayStatusTask());
NS_DispatchToMainThread(new RequestPlayStatusTask());
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "PropertyChanged")) {
ParsePropertyChange(aMsg,
@ -1697,7 +1704,7 @@ BluetoothDBusService::StopInternal()
MOZ_ASSERT(!NS_IsMainThread());
{
MonitorAutoLock lock(sStopBluetoothMonitor);
MonitorAutoLock lock(*sStopBluetoothMonitor);
if (sConnectedDeviceCount > 0) {
lock.Wait(PR_SecondsToInterval(TIMEOUT_FORCE_TO_DISABLE_BT));
}
@ -2656,55 +2663,6 @@ BluetoothDBusService::IsConnected(const uint16_t aProfileId)
return profile->IsConnected();
}
class ConnectBluetoothSocketRunnable : public nsRunnable
{
public:
ConnectBluetoothSocketRunnable(BluetoothReplyRunnable* aRunnable,
UnixSocketConsumer* aConsumer,
const nsAString& aObjectPath,
const nsAString& aServiceUUID,
BluetoothSocketType aType,
bool aAuth,
bool aEncrypt,
int aChannel)
: mRunnable(dont_AddRef(aRunnable))
, mConsumer(aConsumer)
, mObjectPath(aObjectPath)
, mServiceUUID(aServiceUUID)
, mType(aType)
, mAuth(aAuth)
, mEncrypt(aEncrypt)
, mChannel(aChannel)
{
}
nsresult
Run()
{
MOZ_ASSERT(NS_IsMainThread());
nsString address = GetAddressFromObjectPath(mObjectPath);
BluetoothUnixSocketConnector* c =
new BluetoothUnixSocketConnector(mType, mChannel, mAuth, mEncrypt);
if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(address).get())) {
NS_NAMED_LITERAL_STRING(errorStr, "SocketConnectionError");
DispatchBluetoothReply(mRunnable, BluetoothValue(), errorStr);
return NS_ERROR_FAILURE;
}
return NS_OK;
}
private:
nsRefPtr<BluetoothReplyRunnable> mRunnable;
nsRefPtr<UnixSocketConsumer> mConsumer;
nsString mObjectPath;
nsString mServiceUUID;
BluetoothSocketType mType;
bool mAuth;
bool mEncrypt;
int mChannel;
};
class OnUpdateSdpRecordsRunnable : public nsRunnable
{
public:

View File

@ -162,6 +162,9 @@ public:
SendInputMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
protected:
BluetoothDBusService();
~BluetoothDBusService();
private:
/**
@ -196,7 +199,6 @@ private:
void UpdateNotification(ControlEventId aEventId, uint64_t aData);
void DisconnectAllAcls(const nsAString& aAdapterPath);
};
END_BLUETOOTH_NAMESPACE

View File

@ -48,7 +48,7 @@ endif
ifdef MOZ_B2G_FM
DOM_SRCDIRS += \
dom/fm \
dom/fmradio \
$(NULL)
endif

View File

@ -1,4 +0,0 @@
# DOMFMRadio.js
component {901f8a83-03a6-4be9-bb8f-35387d3849da} DOMFMRadioChild.js
contract @mozilla.org/domfmradio;1 {901f8a83-03a6-4be9-bb8f-35387d3849da}
category JavaScript-navigator-property mozFMRadio @mozilla.org/domfmradio;1

View File

@ -1,423 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"
let DEBUG = 0;
if (DEBUG)
debug = function (s) { dump("-*- DOMFMRadioChild: " + s + "\n"); };
else
debug = function (s) { };
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/DOMRequestHelper.jsm");
const DOMFMMANAGER_CONTRACTID = "@mozilla.org/domfmradio;1";
const DOMFMMANAGER_CID = Components.ID("{901f8a83-03a6-4be9-bb8f-35387d3849da}");
XPCOMUtils.defineLazyGetter(Services, "DOMRequest", function() {
return Cc["@mozilla.org/dom/dom-request-service;1"]
.getService(Ci.nsIDOMRequestService);
});
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
function DOMFMRadioChild() { }
DOMFMRadioChild.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
classID: DOMFMMANAGER_CID,
classInfo: XPCOMUtils.generateCI({
classID: DOMFMMANAGER_CID,
contractID: DOMFMMANAGER_CONTRACTID,
classDescription: "DOMFMRadio",
interfaces: [Ci.nsIDOMFMRadio],
flags: Ci.nsIClassInfo.DOM_OBJECT
}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMFMRadio,
Ci.nsIDOMGlobalPropertyInitializer,
Ci.nsISupportsWeakReference]),
// nsIDOMGlobalPropertyInitializer implementation
init: function(aWindow) {
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let perm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "fmradio");
this._hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
if (!this._hasPrivileges) {
Cu.reportError("NO FMRADIO PERMISSION FOR: " + aWindow.document.nodePrincipal.origin + "\n");
return null;
}
const messages = ["DOMFMRadio:enable:Return:OK",
"DOMFMRadio:enable:Return:NO",
"DOMFMRadio:disable:Return:OK",
"DOMFMRadio:disable:Return:NO",
"DOMFMRadio:setFrequency:Return:OK",
"DOMFMRadio:setFrequency:Return:NO",
"DOMFMRadio:seekUp:Return:OK",
"DOMFMRadio:seekUp:Return:NO",
"DOMFMRadio:seekDown:Return:OK",
"DOMFMRadio:seekDown:Return:NO",
"DOMFMRadio:cancelSeek:Return:OK",
"DOMFMRadio:cancelSeek:Return:NO",
"DOMFMRadio:frequencyChange",
"DOMFMRadio:powerStateChange",
"DOMFMRadio:antennaChange"];
this.initDOMRequestHelper(aWindow, messages);
let els = Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService);
els.addSystemEventListener(aWindow, "visibilitychange",
this._updateVisibility.bind(this),
/* useCapture = */ true);
this._visibility = aWindow.document.visibilityState;
// Unlike the |enabled| getter, this is true if *this* DOM window
// has successfully enabled the FM radio more recently than
// disabling it.
this._haveEnabledRadio = false;
},
// Called from DOMRequestIpcHelper
uninit: function() {
this._onFrequencyChange = null;
this._onAntennaChange = null;
this._onDisabled = null;
this._onEnabled = null;
},
_createEvent: function(name) {
return new this._window.Event(name);
},
_sendMessageForRequest: function(name, data, request) {
let id = this.getRequestId(request);
cpmm.sendAsyncMessage(name, {
data: data,
rid: id,
mid: this._id
});
},
_fireFrequencyChangeEvent: function() {
let e = this._createEvent("frequencychange");
if (this._onFrequencyChange) {
this._onFrequencyChange.handleEvent(e);
}
this.dispatchEvent(e);
},
_firePowerStateChangeEvent: function() {
let _enabled = this.enabled;
debug("Current power state: " + _enabled);
if (_enabled) {
let e = this._createEvent("enabled");
if (this._onEnabled) {
this._onEnabled.handleEvent(e);
}
this.dispatchEvent(e);
} else {
let e = this._createEvent("disabled");
if (this._onDisabled) {
this._onDisabled.handleEvent(e);
}
this.dispatchEvent(e);
}
},
_fireAntennaAvailableChangeEvent: function() {
let e = this._createEvent("antennaavailablechange");
if (this._onAntennaChange) {
this._onAntennaChange.handleEvent(e);
}
this.dispatchEvent(e);
},
_updateVisibility: function(evt) {
this._visibility = evt.target.visibilityState;
// Only notify visibility state when we "own" the radio stream.
if (this._haveEnabledRadio) {
this._notifyVisibility();
}
},
_notifyVisibility: function() {
cpmm.sendAsyncMessage("DOMFMRadio:updateVisibility", this._visibility);
},
receiveMessage: function(aMessage) {
let msg = aMessage.json;
if (msg.mid && msg.mid != this._id) {
return;
}
let request;
switch (aMessage.name) {
case "DOMFMRadio:enable:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:enable:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request, "Failed to turn on the FM radio");
break;
case "DOMFMRadio:disable:Return:OK":
this._haveEnabledRadio = false;
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:disable:Return:NO":
// If disabling the radio failed, but the hardware is still
// on, this DOM window is still responsible for the continued
// playback.
this._haveEnabledRadio = this.enabled;
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request,
"Failed to turn off the FM radio");
break;
case "DOMFMRadio:setFrequency:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:setFrequency:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request,
"Failed to set the FM radio frequency");
break;
case "DOMFMRadio:seekUp:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:seekUp:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request, "FM radio seek-up failed");
break;
case "DOMFMRadio:seekDown:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:seekDown:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request, "FM radio seek-down failed");
break;
case "DOMFMRadio:cancelSeek:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:cancelSeek:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request, "Failed to cancel seek");
break;
case "DOMFMRadio:powerStateChange":
this._firePowerStateChangeEvent();
break;
case "DOMFMRadio:frequencyChange":
this._fireFrequencyChangeEvent();
break;
case "DOMFMRadio:antennaChange":
this._fireAntennaAvailableChangeEvent();
break;
}
},
_call: function(name, arg) {
var request = this.createRequest();
this._sendMessageForRequest("DOMFMRadio:" + name, arg, request);
return request;
},
// nsIDOMFMRadio
get enabled() {
return cpmm.sendSyncMessage("DOMFMRadio:getPowerState")[0];
},
get antennaAvailable() {
return cpmm.sendSyncMessage("DOMFMRadio:getAntennaState")[0];
},
get frequency() {
return cpmm.sendSyncMessage("DOMFMRadio:getFrequency")[0];
},
get frequencyUpperBound() {
let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
return range.upper;
},
get frequencyLowerBound() {
let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
return range.lower;
},
get channelWidth() {
let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
return range.channelWidth;
},
set onantennaavailablechange(callback) {
this._onAntennaChange = callback;
},
set onenabled(callback) {
this._onEnabled = callback;
},
set ondisabled(callback) {
this._onDisabled = callback;
},
set onfrequencychange(callback) {
this._onFrequencyChange = callback;
},
disable: function nsIDOMFMRadio_disable() {
return this._call("disable", null);
},
enable: function nsIDOMFMRadio_enable(frequency) {
// FMRadio::Enable() needs the most recent visibility state
// synchronously.
this._haveEnabledRadio = true;
this._notifyVisibility();
return this._call("enable", frequency);
},
setFrequency: function nsIDOMFMRadio_setFreq(frequency) {
return this._call("setFrequency", frequency);
},
seekDown: function nsIDOMFMRadio_seekDown() {
return this._call("seekDown", null);
},
seekUp: function nsIDOMFMRadio_seekUp() {
return this._call("seekUp", null);
},
cancelSeek: function nsIDOMFMRadio_cancelSeek() {
return this._call("cancelSeek", null);
},
// These are fake implementations, will be replaced by using
// nsJSDOMEventTargetHelper, see bug 731746
addEventListener: function(type, listener, useCapture) {
if (!this._eventListenersByType) {
this._eventListenersByType = {};
}
if (!listener) {
return;
}
var listeners = this._eventListenersByType[type];
if (!listeners) {
listeners = this._eventListenersByType[type] = [];
}
useCapture = !!useCapture;
for (let i = 0, len = listeners.length; i < len; i++) {
let l = listeners[i];
if (l && l.listener === listener && l.useCapture === useCapture) {
return;
}
}
listeners.push({
listener: listener,
useCapture: useCapture
});
},
removeEventListener: function(type, listener, useCapture) {
if (!this._eventListenersByType) {
return;
}
useCapture = !!useCapture;
var listeners = this._eventListenersByType[type];
if (listeners) {
for (let i = 0, len = listeners.length; i < len; i++) {
let l = listeners[i];
if (l && l.listener === listener && l.useCapture === useCapture) {
listeners.splice(i, 1);
}
}
}
},
dispatchEvent: function(evt) {
if (!this._eventListenersByType) {
return;
}
let type = evt.type;
var listeners = this._eventListenersByType[type];
if (listeners) {
for (let i = 0, len = listeners.length; i < len; i++) {
let listener = listeners[i].listener;
try {
if (typeof listener == "function") {
listener.call(this, evt);
} else if (listener && listener.handleEvent &&
typeof listener.handleEvent == "function") {
listener.handleEvent(evt);
}
} catch (e) {
debug("Exception is caught: " + e);
}
}
}
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFMRadioChild]);

View File

@ -1,472 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"
let DEBUG = 0;
if (DEBUG)
debug = function(s) { dump("-*- DOMFMRadioParent component: " + s + "\n"); };
else
debug = function(s) {};
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
const MOZ_SETTINGS_CHANGED_OBSERVER_TOPIC = "mozsettings-changed";
const PROFILE_BEFORE_CHANGE_OBSERVER_TOPIC = "profile-before-change";
const BAND_87500_108000_kHz = 1;
const BAND_76000_108000_kHz = 2;
const BAND_76000_90000_kHz = 3;
const FM_BANDS = { };
FM_BANDS[BAND_76000_90000_kHz] = {
lower: 76000,
upper: 90000
};
FM_BANDS[BAND_87500_108000_kHz] = {
lower: 87500,
upper: 108000
};
FM_BANDS[BAND_76000_108000_kHz] = {
lower: 76000,
upper: 108000
};
const BAND_SETTING_KEY = "fmRadio.band";
const CHANNEL_WIDTH_SETTING_KEY = "fmRadio.channelWidth";
// Hal types
const CHANNEL_WIDTH_200KHZ = 200;
const CHANNEL_WIDTH_100KHZ = 100;
const CHANNEL_WIDTH_50KHZ = 50;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageListenerManager");
XPCOMUtils.defineLazyGetter(this, "FMRadio", function() {
return Cc["@mozilla.org/fmradio;1"].getService(Ci.nsIFMRadio);
});
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
"@mozilla.org/settingsService;1",
"nsISettingsService");
this.EXPORTED_SYMBOLS = ["DOMFMRadioParent"];
this.DOMFMRadioParent = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISettingsServiceCallback]),
_initialized: false,
/* Indicates if the FM radio is currently enabled */
_isEnabled: false,
/* Indicates if the FM radio is currently being enabled */
_enabling: false,
/* Current frequency in KHz */
_currentFrequency: 0,
/* Current band setting */
_currentBand: BAND_87500_108000_kHz,
/* Current channel width */
_currentWidth: CHANNEL_WIDTH_100KHZ,
/* Indicates if the antenna is currently available */
_antennaAvailable: true,
_seeking: false,
_seekingCallback: null,
init: function() {
if (this._initialized === true) {
return;
}
this._initialized = true;
this._messages = ["DOMFMRadio:enable", "DOMFMRadio:disable",
"DOMFMRadio:setFrequency", "DOMFMRadio:getCurrentBand",
"DOMFMRadio:getPowerState", "DOMFMRadio:getFrequency",
"DOMFMRadio:getAntennaState",
"DOMFMRadio:seekUp", "DOMFMRadio:seekDown",
"DOMFMRadio:cancelSeek",
"DOMFMRadio:updateVisibility",
];
this._messages.forEach(function(msgName) {
ppmm.addMessageListener(msgName, this);
}.bind(this));
Services.obs.addObserver(this, PROFILE_BEFORE_CHANGE_OBSERVER_TOPIC, false);
Services.obs.addObserver(this, MOZ_SETTINGS_CHANGED_OBSERVER_TOPIC, false);
this._updatePowerState();
// Get the band setting and channel width setting
let lock = gSettingsService.createLock();
lock.get(BAND_SETTING_KEY, this);
lock.get(CHANNEL_WIDTH_SETTING_KEY, this);
this._updateAntennaState();
let self = this;
FMRadio.onantennastatechange = function onantennachange() {
self._updateAntennaState();
};
debug("Initialized");
},
// nsISettingsServiceCallback
handle: function(aName, aResult) {
if (aName == BAND_SETTING_KEY) {
this._updateBand(aResult);
} else if (aName == CHANNEL_WIDTH_SETTING_KEY) {
this._updateChannelWidth(aResult);
}
},
handleError: function(aErrorMessage) {
this._updateBand(BAND_87500_108000_kHz);
this._updateChannelWidth(CHANNEL_WIDTH_100KHZ);
},
_updateAntennaState: function() {
let antennaState = FMRadio.isAntennaAvailable;
if (antennaState != this._antennaAvailable) {
this._antennaAvailable = antennaState;
ppmm.broadcastAsyncMessage("DOMFMRadio:antennaChange", { });
}
},
_updateBand: function(band) {
switch (parseInt(band)) {
case BAND_87500_108000_kHz:
case BAND_76000_108000_kHz:
case BAND_76000_90000_kHz:
this._currentBand = band;
break;
}
},
_updateChannelWidth: function(channelWidth) {
switch (parseInt(channelWidth)) {
case CHANNEL_WIDTH_50KHZ:
case CHANNEL_WIDTH_100KHZ:
case CHANNEL_WIDTH_200KHZ:
this._currentWidth = channelWidth;
break;
}
},
/**
* Update and cache the current frequency.
* Send frequency change message if the frequency is changed.
* The returned boolean value indicates if the frequency is changed.
*/
_updateFrequency: function() {
let frequency = FMRadio.frequency;
if (frequency != this._currentFrequency) {
this._currentFrequency = frequency;
ppmm.broadcastAsyncMessage("DOMFMRadio:frequencyChange", { });
return true;
}
return false;
},
/**
* Update and cache the power state of the FM radio.
* Send message if the power state is changed.
*/
_updatePowerState: function() {
let enabled = FMRadio.enabled;
if (this._isEnabled != enabled) {
this._isEnabled = enabled;
ppmm.broadcastAsyncMessage("DOMFMRadio:powerStateChange", { });
// If the FM radio is enabled, update the current frequency immediately,
if (enabled) {
this._updateFrequency();
}
}
},
_onSeekComplete: function(success) {
if (this._seeking) {
this._seeking = false;
if (this._seekingCallback) {
this._seekingCallback(success);
this._seekingCallback = null;
}
}
},
/**
* Seek the next channel with given direction.
* Only one seek action is allowed at once.
*/
_seekStation: function(direction, aMessage) {
let msg = aMessage.json || { };
let messageName = aMessage.name + ":Return";
// If the FM radio is disabled, do not execute the seek action.
if(!this._isEnabled) {
this._sendMessage(messageName, false, null, msg);
return;
}
let self = this;
function callback(success) {
debug("Seek completed.");
if (!success) {
self._sendMessage(messageName, false, null, msg);
} else {
// Make sure the FM app will get the right frequency.
self._updateFrequency();
self._sendMessage(messageName, true, null, msg);
}
}
if (this._seeking) {
// Pass a boolean value to the callback which indicates that
// the seek action failed.
callback(false);
return;
}
this._seekingCallback = callback;
this._seeking = true;
let self = this;
FMRadio.seek(direction);
FMRadio.addEventListener("seekcomplete", function FM_onSeekComplete() {
FMRadio.removeEventListener("seekcomplete", FM_onSeekComplete);
self._onSeekComplete(true);
});
},
/**
* Round the frequency to match the range of frequency and the channel width.
* If the given frequency is out of range, return null.
* For example:
* - lower: 87.5MHz, upper: 108MHz, channel width: 0.2MHz
* 87600 is rounded to 87700
* 87580 is rounded to 87500
* 109000 is not rounded, null will be returned
*/
_roundFrequency: function(frequencyInKHz) {
if (frequencyInKHz < FM_BANDS[this._currentBand].lower ||
frequencyInKHz > FM_BANDS[this._currentBand].upper) {
return null;
}
let partToBeRounded = frequencyInKHz - FM_BANDS[this._currentBand].lower;
let roundedPart = Math.round(partToBeRounded / this._currentWidth) *
this._currentWidth;
return FM_BANDS[this._currentBand].lower + roundedPart;
},
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case PROFILE_BEFORE_CHANGE_OBSERVER_TOPIC:
this._messages.forEach(function(msgName) {
ppmm.removeMessageListener(msgName, this);
}.bind(this));
Services.obs.removeObserver(this, PROFILE_BEFORE_CHANGE_OBSERVER_TOPIC);
Services.obs.removeObserver(this, MOZ_SETTINGS_CHANGED_OBSERVER_TOPIC);
ppmm = null;
this._messages = null;
break;
case MOZ_SETTINGS_CHANGED_OBSERVER_TOPIC:
let setting = JSON.parse(aData);
this.handleMozSettingsChanged(setting);
break;
}
},
_sendMessage: function(message, success, data, msg) {
msg.manager.sendAsyncMessage(message + (success ? ":OK" : ":NO"), {
data: data,
rid: msg.rid,
mid: msg.mid
});
},
handleMozSettingsChanged: function(settings) {
switch (settings.key) {
case BAND_SETTING_KEY:
this._updateBand(settings.value);
break;
case CHANNEL_WIDTH_SETTING_KEY:
this._updateChannelWidth(settings.value);
break;
}
},
_enableFMRadio: function(msg) {
let frequencyInKHz = this._roundFrequency(msg.data * 1000);
// If the FM radio is already enabled or it is currently being enabled
// or the given frequency is out of range, return false.
if (this._isEnabled || this._enabling || !frequencyInKHz) {
this._sendMessage("DOMFMRadio:enable:Return", false, null, msg);
return;
}
this._enabling = true;
let self = this;
FMRadio.addEventListener("enabled", function on_enabled() {
dump("Perf:FMRadio:Enable " + (Date.now()- timeStart) + " ms.\n");
self._enabling = false;
FMRadio.removeEventListener("enabled", on_enabled);
// To make sure the FM app will get right frequency after the FM
// radio is enabled, we have to set the frequency first.
FMRadio.setFrequency(frequencyInKHz);
// Update the current frequency without sending 'frequencyChange'
// msg, to make sure the FM app will get the right frequency when the
// 'enabled' event is fired.
self._currentFrequency = FMRadio.frequency;
self._updatePowerState();
self._sendMessage("DOMFMRadio:enable:Return", true, null, msg);
// The frequency is changed from 'null' to some number, so we should
// send the 'frequencyChange' message manually.
ppmm.broadcastAsyncMessage("DOMFMRadio:frequencyChange", { });
});
let timeStart = Date.now();
FMRadio.enable({
lowerLimit: FM_BANDS[self._currentBand].lower,
upperLimit: FM_BANDS[self._currentBand].upper,
channelWidth: self._currentWidth // 100KHz by default
});
},
_disableFMRadio: function(msg) {
// If the FM radio is already disabled, return false.
if (!this._isEnabled) {
this._sendMessage("DOMFMRadio:disable:Return", false, null, msg);
return;
}
let self = this;
FMRadio.addEventListener("disabled", function on_disabled() {
debug("FM Radio is disabled!");
FMRadio.removeEventListener("disabled", on_disabled);
self._updatePowerState();
self._sendMessage("DOMFMRadio:disable:Return", true, null, msg);
// If the FM Radio is currently seeking, no fail-to-seek or similar
// event will be fired, execute the seek callback manually.
self._onSeekComplete(false);
});
FMRadio.disable();
},
receiveMessage: function(aMessage) {
let msg = aMessage.json || {};
msg.manager = aMessage.target;
let ret = 0;
let self = this;
if (!aMessage.target.assertPermission("fmradio")) {
Cu.reportError("FMRadio message " + aMessage.name +
" from a content process with no 'fmradio' privileges.");
return null;
}
switch (aMessage.name) {
case "DOMFMRadio:enable":
self._enableFMRadio(msg);
break;
case "DOMFMRadio:disable":
self._disableFMRadio(msg);
break;
case "DOMFMRadio:setFrequency":
let frequencyInKHz = self._roundFrequency(msg.data * 1000);
// If the FM radio is disabled or the given frequency is out of range,
// skip to set frequency and send back the False message immediately.
if (!self._isEnabled || !frequencyInKHz) {
self._sendMessage("DOMFMRadio:setFrequency:Return", false, null, msg);
} else {
FMRadio.setFrequency(frequencyInKHz);
self._sendMessage("DOMFMRadio:setFrequency:Return", true, null, msg);
this._updateFrequency();
}
break;
case "DOMFMRadio:getCurrentBand":
// this message is sync
return {
lower: FM_BANDS[self._currentBand].lower / 1000, // in MHz
upper: FM_BANDS[self._currentBand].upper / 1000, // in MHz
channelWidth: self._currentWidth / 1000 // in MHz
};
case "DOMFMRadio:getPowerState":
// this message is sync
return self._isEnabled;
case "DOMFMRadio:getFrequency":
// this message is sync
return self._isEnabled ? this._currentFrequency / 1000 : null; // in MHz
case "DOMFMRadio:getAntennaState":
// this message is sync
return self._antennaAvailable;
case "DOMFMRadio:seekUp":
self._seekStation(Ci.nsIFMRadio.SEEK_DIRECTION_UP, aMessage);
break;
case "DOMFMRadio:seekDown":
self._seekStation(Ci.nsIFMRadio.SEEK_DIRECTION_DOWN, aMessage);
break;
case "DOMFMRadio:cancelSeek":
// If the FM radio is disabled, or the FM radio is not currently
// seeking, do not execute the cancel seek action.
if (!self._isEnabled || !self._seeking) {
self._sendMessage("DOMFMRadio:cancelSeek:Return", false, null, msg);
} else {
FMRadio.cancelSeek();
// No fail-to-seek or similar event will be fired from the hal part,
// so execute the seek callback here manually.
this._onSeekComplete(false);
// The FM radio will stop at one frequency without any event, so we need to
// update the current frequency, make sure the FM app will get the right frequency.
this._updateFrequency();
self._sendMessage("DOMFMRadio:cancelSeek:Return", true, null, msg);
}
break;
case "DOMFMRadio:updateVisibility":
FMRadio.updateVisible(msg == 'visible');
break;
}
}
};
DOMFMRadioParent.init();

View File

@ -1,264 +0,0 @@
/* 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 "mozilla/Hal.h"
#include "mozilla/HalTypes.h"
#include "mozilla/Preferences.h"
#include "nsIAudioManager.h"
#include "FMRadio.h"
#include "nsDOMEvent.h"
#include "nsDOMClassInfo.h"
#include "nsFMRadioSettings.h"
#include "nsCOMPtr.h"
#undef LOG
#if defined(MOZ_WIDGET_GONK)
#include <android/log.h>
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "FMRadio" , ## args)
#else
#define LOG(args...)
#endif
// The pref indicates if the device has an internal antenna.
// If the pref is true, the antanna will be always available.
#define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fm.antenna.internal"
#define RADIO_SEEK_COMPLETE_EVENT_NAME NS_LITERAL_STRING("seekcomplete")
#define RADIO_DISABLED_EVENT_NAME NS_LITERAL_STRING("disabled")
#define RADIO_ENABLED_EVENT_NAME NS_LITERAL_STRING("enabled")
#define ANTENNA_STATE_CHANGED_EVENT_NAME NS_LITERAL_STRING("antennastatechange")
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
using namespace mozilla::dom::fm;
using namespace mozilla::hal;
using mozilla::Preferences;
FMRadio::FMRadio()
: mHeadphoneState(SWITCH_STATE_OFF)
, mHasInternalAntenna(false)
, mHidden(true)
{
LOG("FMRadio is initialized.");
mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF,
/* default = */ false);
if (mHasInternalAntenna) {
LOG("We have an internal antenna.");
} else {
RegisterSwitchObserver(SWITCH_HEADPHONES, this);
mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES);
}
RegisterFMRadioObserver(this);
}
FMRadio::~FMRadio()
{
UnregisterFMRadioObserver(this);
if (!mHasInternalAntenna) {
UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
}
}
DOMCI_DATA(FMRadio, FMRadio)
NS_INTERFACE_MAP_BEGIN(FMRadio)
NS_INTERFACE_MAP_ENTRY(nsIFMRadio)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FMRadio)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_EVENT_HANDLER(FMRadio, seekcomplete)
NS_IMPL_EVENT_HANDLER(FMRadio, disabled)
NS_IMPL_EVENT_HANDLER(FMRadio, enabled)
NS_IMPL_EVENT_HANDLER(FMRadio, antennastatechange)
NS_IMPL_ADDREF_INHERITED(FMRadio, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(FMRadio, nsDOMEventTargetHelper)
/* readonly attribute boolean isAntennaAvailable; */
NS_IMETHODIMP FMRadio::GetIsAntennaAvailable(bool *aIsAvailable)
{
if (mHasInternalAntenna) {
*aIsAvailable = true;
} else {
*aIsAvailable = mHeadphoneState != SWITCH_STATE_OFF;
}
return NS_OK;
}
/* readonly attribute long frequency; */
NS_IMETHODIMP FMRadio::GetFrequency(int32_t *aFrequency)
{
*aFrequency = GetFMRadioFrequency();
return NS_OK;
}
/* readonly attribute blean enabled; */
NS_IMETHODIMP FMRadio::GetEnabled(bool *aEnabled)
{
*aEnabled = IsFMRadioOn();
return NS_OK;
}
/* void enable (in nsIFMRadioSettings settings); */
NS_IMETHODIMP FMRadio::Enable(nsIFMRadioSettings *settings)
{
hal::FMRadioSettings info;
int32_t upperLimit, lowerLimit, channelWidth;
if (!mAudioChannelAgent) {
nsresult rv;
mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
if (!mAudioChannelAgent) {
return NS_ERROR_FAILURE;
}
mAudioChannelAgent->Init(AUDIO_CHANNEL_CONTENT, this);
}
bool canPlay;
mAudioChannelAgent->SetVisibilityState(!mHidden);
mAudioChannelAgent->StartPlaying(&canPlay);
settings->GetUpperLimit(&upperLimit);
settings->GetLowerLimit(&lowerLimit);
settings->GetChannelWidth(&channelWidth);
info.upperLimit() = upperLimit;
info.lowerLimit() = lowerLimit;
info.spaceType() = channelWidth;
EnableFMRadio(info);
nsCOMPtr<nsIAudioManager> audioManager =
do_GetService(NS_AUDIOMANAGER_CONTRACTID);
NS_ENSURE_TRUE(audioManager, NS_OK);
audioManager->SetFmRadioAudioEnabled(true);
// We enable the hardware, but mute the audio stream, in order to
// simplify state handling. This is simpler but worse for battery
// life; followup is bug 820282.
// Note: To adjust FM volume is only available after setting up
// routing patch.
CanPlayChanged(canPlay);
return NS_OK;
}
/* void disableRadio (); */
NS_IMETHODIMP FMRadio::Disable()
{
// Fix Bug 796733.
// DisableFMRadio should be called before SetFmRadioAudioEnabled to prevent
// the annoying beep sound.
DisableFMRadio();
nsCOMPtr<nsIAudioManager> audioManager =
do_GetService(NS_AUDIOMANAGER_CONTRACTID);
NS_ENSURE_TRUE(audioManager, NS_OK);
audioManager->SetFmRadioAudioEnabled(false);
if (mAudioChannelAgent) {
mAudioChannelAgent->StopPlaying();
mAudioChannelAgent = nullptr;
}
return NS_OK;
}
/* void cancelSeek */
NS_IMETHODIMP FMRadio::CancelSeek()
{
CancelFMRadioSeek();
return NS_OK;
}
/* void seek (in long direction); */
NS_IMETHODIMP FMRadio::Seek(int32_t direction)
{
if (direction == (int)FM_RADIO_SEEK_DIRECTION_UP) {
FMRadioSeek(FM_RADIO_SEEK_DIRECTION_UP);
} else {
FMRadioSeek(FM_RADIO_SEEK_DIRECTION_DOWN);
}
return NS_OK;
}
/* nsIFMRadioSettings getSettings (); */
NS_IMETHODIMP FMRadio::GetSettings(nsIFMRadioSettings * *_retval)
{
hal::FMRadioSettings settings;
GetFMRadioSettings(&settings);
nsCOMPtr<nsIFMRadioSettings> radioSettings(new nsFMRadioSettings(
settings.upperLimit(),
settings.lowerLimit(),
settings.spaceType()));
radioSettings.forget(_retval);
return NS_OK;
}
/* void setFrequency (in long frequency); */
NS_IMETHODIMP FMRadio::SetFrequency(int32_t frequency)
{
SetFMRadioFrequency(frequency);
return NS_OK;
}
NS_IMETHODIMP FMRadio::UpdateVisible(bool aVisible)
{
mHidden = !aVisible;
if (mAudioChannelAgent) {
mAudioChannelAgent->SetVisibilityState(!mHidden);
}
return NS_OK;
}
void FMRadio::Notify(const SwitchEvent& aEvent)
{
if (mHeadphoneState != aEvent.status()) {
LOG("Antenna state is changed!");
mHeadphoneState = aEvent.status();
DispatchTrustedEvent(ANTENNA_STATE_CHANGED_EVENT_NAME);
}
}
void FMRadio::Notify(const FMRadioOperationInformation& info)
{
switch (info.operation())
{
case FM_RADIO_OPERATION_ENABLE:
DispatchTrustedEvent(RADIO_ENABLED_EVENT_NAME);
break;
case FM_RADIO_OPERATION_DISABLE:
DispatchTrustedEvent(RADIO_DISABLED_EVENT_NAME);
break;
case FM_RADIO_OPERATION_SEEK:
DispatchTrustedEvent(RADIO_SEEK_COMPLETE_EVENT_NAME);
break;
default:
MOZ_CRASH();
}
}
/* void canPlayChanged (in boolean canPlay); */
NS_IMETHODIMP FMRadio::CanPlayChanged(bool canPlay)
{
nsCOMPtr<nsIAudioManager> audioManager =
do_GetService(NS_AUDIOMANAGER_CONTRACTID);
NS_ENSURE_TRUE(audioManager, NS_OK);
bool AudioEnabled;
audioManager->GetFmRadioAudioEnabled(&AudioEnabled);
if (AudioEnabled == canPlay) {
return NS_OK;
}
/* mute fm first, it should be better to stop&resume fm */
audioManager->SetFmRadioAudioEnabled(canPlay);
return NS_OK;
}

View File

@ -1,55 +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/. */
#ifndef mozilla_dom_fm_radio_h__
#define mozilla_dom_fm_radio_h__
#include "nsCOMPtr.h"
#include "mozilla/HalTypes.h"
#include "nsDOMEventTargetHelper.h"
#include "nsIFMRadio.h"
#include "AudioChannelService.h"
#define NS_FMRADIO_CONTRACTID "@mozilla.org/fmradio;1"
// 9cb91834-78a9-4029-b644-7806173c5e2d
#define NS_FMRADIO_CID {0x9cb91834, 0x78a9, 0x4029, \
{0xb6, 0x44, 0x78, 0x06, 0x17, 0x3c, 0x5e, 0x2d}}
namespace mozilla {
namespace dom {
namespace fm {
/* Header file */
class FMRadio : public nsDOMEventTargetHelper
, public nsIFMRadio
, public hal::FMRadioObserver
, public hal::SwitchObserver
, public nsIAudioChannelAgentCallback
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIFMRADIO
NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
FMRadio();
virtual void Notify(const hal::FMRadioOperationInformation& info);
virtual void Notify(const hal::SwitchEvent& aEvent);
private:
~FMRadio();
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
hal::SwitchState mHeadphoneState;
bool mHasInternalAntenna;
bool mHidden;
};
} // namespace fm
} // namespace dom
} // namespace mozilla
#endif

View File

@ -1,61 +0,0 @@
/* 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 "nsFMRadioSettings.h"
NS_IMPL_ISUPPORTS1(nsFMRadioSettings, nsIFMRadioSettings)
nsFMRadioSettings::nsFMRadioSettings(int32_t aUpperLimit,
int32_t aLowerLimit,
int32_t aChannelWidth)
{
mUpperLimit = aUpperLimit;
mLowerLimit = aLowerLimit;
mChannelWidth = aChannelWidth;
}
nsFMRadioSettings::~nsFMRadioSettings()
{
}
/* attribute long upperLimit; */
NS_IMETHODIMP nsFMRadioSettings::GetUpperLimit(int32_t *aUpperLimit)
{
*aUpperLimit = mUpperLimit;
return NS_OK;
}
NS_IMETHODIMP nsFMRadioSettings::SetUpperLimit(int32_t aUpperLimit)
{
mUpperLimit = aUpperLimit;
return NS_OK;
}
/* attribute long lowerLimit; */
NS_IMETHODIMP nsFMRadioSettings::GetLowerLimit(int32_t *aLowerLimit)
{
*aLowerLimit = mLowerLimit;
return NS_OK;
}
NS_IMETHODIMP nsFMRadioSettings::SetLowerLimit(int32_t aLowerLimit)
{
mLowerLimit = aLowerLimit;
return NS_OK;
}
/* attribute long spaceType; */
NS_IMETHODIMP nsFMRadioSettings::GetChannelWidth(int32_t *aChannelWidth)
{
*aChannelWidth = mChannelWidth;
return NS_OK;
}
NS_IMETHODIMP nsFMRadioSettings::SetChannelWidth(int32_t aChannelWidth)
{
mChannelWidth = aChannelWidth;
return NS_OK;
}

View File

@ -1,26 +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/. */
#ifndef mozilla_dom_fm_radio_settings_h__
#define mozilla_dom_fm_radio_settings_h__
#include "nsIFMRadio.h"
class nsFMRadioSettings : public nsIFMRadioSettings
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFMRADIOSETTINGS
nsFMRadioSettings(int32_t aUpperLimit, int32_t aLowerLimit, int32_t aChannelWidth);
private:
~nsFMRadioSettings();
int32_t mUpperLimit;
int32_t mLowerLimit;
int32_t mChannelWidth;
};
#endif

View File

@ -1,122 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsIDOMDOMRequest.idl"
[scriptable, uuid(1d0443f3-ac30-4f9e-a070-002bb20ce1e6)]
interface nsIDOMFMRadio : nsISupports {
/* Indicates if the FM radio is enabled. */
readonly attribute boolean enabled;
/* Indicates if the antenna is plugged and available. */
readonly attribute boolean antennaAvailable;
/**
* Current frequency in MHz.
* The value will be null if the FM radio is disabled.
*/
readonly attribute jsval frequency;
/* The upper bound of frequency in MHz. */
readonly attribute double frequencyUpperBound;
/* The lower bound of frequency in MHz. */
readonly attribute double frequencyLowerBound;
/**
* The channel width of the ranges of frequency, in MHz.
* Usually, the value is one of:
* - 0.05 MHz
* - 0.1 MHz
* - 0.2 MHz
*/
readonly attribute double channelWidth;
/* Fired when the FM radio is enabled. */
attribute nsIDOMEventListener onenabled;
/* Fired when the FM radio is disabled. */
attribute nsIDOMEventListener ondisabled;
/**
* Fired when the antenna becomes available or unavailable, i.e., fired when
* the antennaAvailable attribute changes.
*/
attribute nsIDOMEventListener onantennaavailablechange;
/* Fired when the FM radio's frequency is changed. */
attribute nsIDOMEventListener onfrequencychange;
/**
* Power the FM radio off.
* The disabled event will be fired if this request completes successfully.
*/
nsIDOMDOMRequest disable();
/**
* Power the FM radio on, and tune the radio to the given frequency in MHz.
* This will fail if the given frequency is out of range.
* The enabled event and frequencychange event will be fired if this request
* completes successfully.
*/
nsIDOMDOMRequest enable(in double frequency);
/**
* Tune the FM radio to the given frequency.
* This will fail if the given frequency is out of range.
*
* Note that the FM radio may not tuned to the exact frequency given. To get
* the frequency the radio is actually tuned to, wait for the request to fire
* onsucess (or wait for the frequencychange event to fire), and then read the
* frequency attribute.
*/
nsIDOMDOMRequest setFrequency(in double frequency);
/**
* Tell the FM radio to seek up to the next channel. If the frequency is
* successfully changed, the frequencychange event will be triggered.
*
* Only one seek is allowed at once:
* If the radio is seeking when the seekUp is called, onerror will be fired.
*/
nsIDOMDOMRequest seekUp();
/**
* Tell the FM radio to seek down to the next channel. If the frequency is
* successfully changed, the frequencychange event will be triggered.
*
* Only one seek is allowed at once:
* If the radio is seeking when the seekDown is called, onerror will be fired.
*/
nsIDOMDOMRequest seekDown();
/**
* Cancel the seek action.
* If the radio is not currently seeking up or down, onerror will be fired.
*/
nsIDOMDOMRequest cancelSeek();
/**
* These functions related to EventTarget are temporary hacks:
* - addEventListener
* - removeEventListener
* - handleEvent
*
* These will be removed by inheriting from nsIJSDOMEventTarget,
* see bug 731746.
*/
[optional_argc] void addEventListener(in DOMString type,
in nsIDOMEventListener listener,
[optional] in boolean useCapture,
[optional] in boolean wantsUntrusted);
void removeEventListener(in DOMString type,
in nsIDOMEventListener listener,
[optional] in boolean useCapture);
boolean dispatchEvent(in nsIDOMEvent evt) raises(DOMException);
};

View File

@ -1,107 +0,0 @@
/* 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"
[scriptable, uuid(c142387a-5488-454b-8b5a-91f0dbee833b)]
interface nsIFMRadioSettings : nsISupports
{
/* Upper limit in KHz */
attribute long upperLimit;
/* Lower limit in KHz */
attribute long lowerLimit;
/* Channel width in KHz */
attribute long channelWidth;
};
/**
* This is an interface to expose the FM radio hardware related functions;
* it's kind of the FM radio hardware wrapper interface.
*
* Because the WebFM API (navigator.mozFMRadio) is implemented as a JS component,
* it can't access our C++ hardware interface directly; instead it must go
* through this interface.
* Do not confuse this interface with the WebFM DOM interface (nsIDOMFMRadio).
*
* If the WebFM API is re-written in c++ some day, this interface will be useless.
*/
[scriptable, builtinclass, uuid(2ee7c122-b7aa-4948-9bc5-e4593ed4ac32)]
interface nsIFMRadio : nsIDOMEventTarget {
const long SEEK_DIRECTION_UP = 0;
const long SEEK_DIRECTION_DOWN = 1;
/**
* Indicates if the FM radio hardware is enabled.
*/
readonly attribute boolean enabled;
/**
* Current frequency in KHz
*/
readonly attribute long frequency;
/**
* Indicates if the antenna is plugged in and available.
*/
readonly attribute boolean isAntennaAvailable;
/**
* Enable the FM radio hardware with the given settings.
*/
void enable(in nsIFMRadioSettings settings);
/**
* Disable the FM radio hardware.
*/
void disable();
/**
* Seek the next available channel (up or down).
*
* @param direction
* The value should be one of SEEK_DIRECTION_DOWN and SEEK_DIRECTION_UP
*/
void seek(in long direction);
/**
* Cancel the seek action.
*/
void cancelSeek();
/**
* Get the current settings.
*/
nsIFMRadioSettings getSettings();
/**
* Set the frequency in KHz
*/
void setFrequency(in long frequency);
/**
* Update the visibility state of our client.
*/
void updateVisible(in boolean visible);
/**
* Fired when the antenna state is changed.
*/
[implicit_jscontext] attribute jsval onantennastatechange;
/**
* Fired when a seek action completes.
*/
[implicit_jscontext] attribute jsval onseekcomplete;
/**
* Fired when the FM radio hardware is enabled.
*/
[implicit_jscontext] attribute jsval onenabled;
/**
* Fired when the FM radio hardware is disabled.
*/
[implicit_jscontext] attribute jsval ondisabled;
};

295
dom/fmradio/FMRadio.cpp Normal file
View File

@ -0,0 +1,295 @@
/* -*- 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 "mozilla/dom/FMRadio.h"
#include "nsContentUtils.h"
#include "mozilla/Hal.h"
#include "mozilla/HalTypes.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/FMRadioBinding.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/PFMRadioChild.h"
#include "mozilla/dom/FMRadioService.h"
#include "DOMRequest.h"
#undef LOG
#define LOG(args...) FM_LOG("FMRadio", args)
// The pref indicates if the device has an internal antenna.
// If the pref is true, the antanna will be always available.
#define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal"
using namespace mozilla::hal;
using mozilla::Preferences;
BEGIN_FMRADIO_NAMESPACE
class FMRadioRequest MOZ_FINAL : public ReplyRunnable
, public DOMRequest
{
public:
NS_DECL_ISUPPORTS_INHERITED
FMRadioRequest(nsPIDOMWindow* aWindow, FMRadio* aFMRadio)
: DOMRequest(aWindow)
{
// |FMRadio| inherits from |nsIDOMEventTarget| and |nsISupportsWeakReference|
// which both inherits from nsISupports, so |nsISupports| is an ambiguous
// base of |FMRadio|, we have to cast |aFMRadio| to one of the base classes.
mFMRadio = do_GetWeakReference(static_cast<nsIDOMEventTarget*>(aFMRadio));
}
~FMRadioRequest() { }
NS_IMETHODIMP
Run()
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIDOMEventTarget> target = do_QueryReferent(mFMRadio);
if (!target) {
return NS_OK;
}
FMRadio* fmRadio = static_cast<FMRadio*>(
static_cast<nsIDOMEventTarget*>(target));
if (fmRadio->mIsShutdown) {
return NS_OK;
}
switch (mResponseType.type()) {
case FMRadioResponseType::TErrorResponse:
FireError(mResponseType.get_ErrorResponse().error());
break;
case FMRadioResponseType::TSuccessResponse:
FireSuccess(JS::UndefinedHandleValue);
break;
default:
MOZ_CRASH();
}
return NS_OK;
}
private:
nsWeakPtr mFMRadio;
};
NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest, DOMRequest)
FMRadio::FMRadio()
: mHeadphoneState(SWITCH_STATE_OFF)
, mHasInternalAntenna(false)
, mIsShutdown(false)
{
LOG("FMRadio is initialized.");
SetIsDOMBinding();
}
FMRadio::~FMRadio()
{
}
void
FMRadio::Init(nsPIDOMWindow *aWindow)
{
BindToOwner(aWindow);
IFMRadioService::Singleton()->AddObserver(this);
mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF,
/* default = */ false);
if (mHasInternalAntenna) {
LOG("We have an internal antenna.");
} else {
mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES);
RegisterSwitchObserver(SWITCH_HEADPHONES, this);
}
}
void
FMRadio::Shutdown()
{
IFMRadioService::Singleton()->RemoveObserver(this);
if (!mHasInternalAntenna) {
UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
}
mIsShutdown = true;
}
JSObject*
FMRadio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return FMRadioBinding::Wrap(aCx, aScope, this);
}
void
FMRadio::Notify(const SwitchEvent& aEvent)
{
MOZ_ASSERT(!mHasInternalAntenna);
if (mHeadphoneState != aEvent.status()) {
mHeadphoneState = aEvent.status();
DispatchTrustedEvent(NS_LITERAL_STRING("antennaavailablechange"));
}
}
void
FMRadio::Notify(const FMRadioEventType& aType)
{
switch (aType) {
case FrequencyChanged:
DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange"));
break;
case EnabledChanged:
if (Enabled()) {
DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
} else {
DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
}
break;
default:
MOZ_CRASH();
}
}
/* static */
bool
FMRadio::Enabled()
{
return IFMRadioService::Singleton()->IsEnabled();
}
bool
FMRadio::AntennaAvailable() const
{
return mHasInternalAntenna ? true : mHeadphoneState != SWITCH_STATE_OFF;
}
Nullable<double>
FMRadio::GetFrequency() const
{
return Enabled() ?
Nullable<double>(IFMRadioService::Singleton()->GetFrequency()) :
Nullable<double>();
}
double
FMRadio::FrequencyUpperBound() const
{
return IFMRadioService::Singleton()->GetFrequencyUpperBound();
}
double
FMRadio::FrequencyLowerBound() const
{
return IFMRadioService::Singleton()->GetFrequencyLowerBound();
}
double
FMRadio::ChannelWidth() const
{
return IFMRadioService::Singleton()->GetChannelWidth();
}
already_AddRefed<DOMRequest>
FMRadio::Enable(double aFrequency)
{
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
if (!win) {
return nullptr;
}
nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
IFMRadioService::Singleton()->Enable(aFrequency, r);
return r.forget();
}
already_AddRefed<DOMRequest>
FMRadio::Disable()
{
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
if (!win) {
return nullptr;
}
nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
IFMRadioService::Singleton()->Disable(r);
return r.forget();
}
already_AddRefed<DOMRequest>
FMRadio::SetFrequency(double aFrequency)
{
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
if (!win) {
return nullptr;
}
nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
IFMRadioService::Singleton()->SetFrequency(aFrequency, r);
return r.forget();
}
already_AddRefed<DOMRequest>
FMRadio::SeekUp()
{
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
if (!win) {
return nullptr;
}
nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_UP, r);
return r.forget();
}
already_AddRefed<DOMRequest>
FMRadio::SeekDown()
{
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
if (!win) {
return nullptr;
}
nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_DOWN, r);
return r.forget();
}
already_AddRefed<DOMRequest>
FMRadio::CancelSeek()
{
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
if (!win) {
return nullptr;
}
nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
IFMRadioService::Singleton()->CancelSeek(r);
return r.forget();
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(FMRadio, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(FMRadio, nsDOMEventTargetHelper)
END_FMRADIO_NAMESPACE

92
dom/fmradio/FMRadio.h Normal file
View File

@ -0,0 +1,92 @@
/* -*- Mode: C++; 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/. */
#ifndef mozilla_dom_FMRadio_h
#define mozilla_dom_FMRadio_h
#include "FMRadioCommon.h"
#include "nsDOMEventTargetHelper.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/HalTypes.h"
#include "nsWeakReference.h"
class nsPIDOMWindow;
class nsIScriptContext;
BEGIN_FMRADIO_NAMESPACE
class DOMRequest;
class FMRadio MOZ_FINAL : public nsDOMEventTargetHelper
, public hal::SwitchObserver
, public FMRadioEventObserver
, public nsSupportsWeakReference
{
friend class FMRadioRequest;
public:
FMRadio();
NS_DECL_ISUPPORTS_INHERITED
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
void Init(nsPIDOMWindow *aWindow);
void Shutdown();
/* hal::SwitchObserver */
virtual void Notify(const hal::SwitchEvent& aEvent) MOZ_OVERRIDE;
/* FMRadioEventObserver */
virtual void Notify(const FMRadioEventType& aType) MOZ_OVERRIDE;
nsPIDOMWindow* GetParentObject() const
{
return GetOwner();
}
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
static bool Enabled();
bool AntennaAvailable() const;
Nullable<double> GetFrequency() const;
double FrequencyUpperBound() const;
double FrequencyLowerBound() const;
double ChannelWidth() const;
already_AddRefed<DOMRequest> Enable(double aFrequency);
already_AddRefed<DOMRequest> Disable();
already_AddRefed<DOMRequest> SetFrequency(double aFrequency);
already_AddRefed<DOMRequest> SeekUp();
already_AddRefed<DOMRequest> SeekDown();
already_AddRefed<DOMRequest> CancelSeek();
IMPL_EVENT_HANDLER(enabled);
IMPL_EVENT_HANDLER(disabled);
IMPL_EVENT_HANDLER(antennaavailablechange);
IMPL_EVENT_HANDLER(frequencychange);
private:
~FMRadio();
hal::SwitchState mHeadphoneState;
bool mHasInternalAntenna;
bool mIsShutdown;
};
END_FMRADIO_NAMESPACE
#endif // mozilla_dom_FMRadio_h

View File

@ -0,0 +1,42 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef FMRADIOCOMMON_H_
#define FMRADIOCOMMON_H_
#include "mozilla/Observer.h"
#undef FM_LOG
#if defined(ANDROID)
#include <android/log.h>
#define FM_LOG(FMRADIO_LOG_INFO, args...) \
__android_log_print(ANDROID_LOG_INFO, \
FMRADIO_LOG_INFO, \
## args)
#else
#define FM_LOG(args...)
#endif
#define BEGIN_FMRADIO_NAMESPACE \
namespace mozilla { namespace dom {
#define END_FMRADIO_NAMESPACE \
} /* namespace dom */ } /* namespace mozilla */
BEGIN_FMRADIO_NAMESPACE
enum FMRadioEventType
{
FrequencyChanged,
EnabledChanged
};
typedef mozilla::Observer<FMRadioEventType> FMRadioEventObserver;
typedef mozilla::ObserverList<FMRadioEventType> FMRadioEventObserverList;
END_FMRADIO_NAMESPACE
#endif /* FMRADIOCOMMON_H_ */

View File

@ -0,0 +1,800 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FMRadioService.h"
#include "mozilla/Hal.h"
#include "nsIAudioManager.h"
#include "AudioManager.h"
#include "nsDOMClassInfo.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/FMRadioChild.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsJSUtils.h"
#include "nsCxPusher.h"
#define BAND_87500_108000_kHz 1
#define BAND_76000_108000_kHz 2
#define BAND_76000_90000_kHz 3
#define CHANNEL_WIDTH_200KHZ 200
#define CHANNEL_WIDTH_100KHZ 100
#define CHANNEL_WIDTH_50KHZ 50
#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
#define SETTING_KEY_RIL_RADIO_DISABLED "ril.radio.disabled"
using namespace mozilla::hal;
using mozilla::Preferences;
BEGIN_FMRADIO_NAMESPACE
// static
IFMRadioService*
IFMRadioService::Singleton()
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
return FMRadioChild::Singleton();
} else {
return FMRadioService::Singleton();
}
}
StaticRefPtr<FMRadioService> FMRadioService::sFMRadioService;
FMRadioService::FMRadioService()
: mPendingFrequencyInKHz(0)
, mState(Disabled)
, mHasReadRilSetting(false)
, mRilDisabled(false)
, mPendingRequest(nullptr)
, mObserverList(FMRadioEventObserverList())
{
// Read power state and frequency from Hal.
mEnabled = IsFMRadioOn();
if (mEnabled) {
mPendingFrequencyInKHz = GetFMRadioFrequency();
SetState(Enabled);
}
switch (Preferences::GetInt("dom.fmradio.band", BAND_87500_108000_kHz)) {
case BAND_76000_90000_kHz:
mUpperBoundInKHz = 90000;
mLowerBoundInKHz = 76000;
break;
case BAND_76000_108000_kHz:
mUpperBoundInKHz = 108000;
mLowerBoundInKHz = 76000;
break;
case BAND_87500_108000_kHz:
default:
mUpperBoundInKHz = 108000;
mLowerBoundInKHz = 87500;
break;
}
switch (Preferences::GetInt("dom.fmradio.channelWidth",
CHANNEL_WIDTH_100KHZ)) {
case CHANNEL_WIDTH_200KHZ:
mChannelWidthInKHz = 200;
break;
case CHANNEL_WIDTH_50KHZ:
mChannelWidthInKHz = 50;
break;
case CHANNEL_WIDTH_100KHZ:
default:
mChannelWidthInKHz = 100;
break;
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_FAILED(obs->AddObserver(this,
MOZSETTINGS_CHANGED_ID,
/* useWeak */ false))) {
NS_WARNING("Failed to add settings change observer!");
}
RegisterFMRadioObserver(this);
}
FMRadioService::~FMRadioService()
{
UnregisterFMRadioObserver(this);
}
class EnableRunnable MOZ_FINAL : public nsRunnable
{
public:
EnableRunnable(int32_t aUpperLimit, int32_t aLowerLimit, int32_t aSpaceType)
: mUpperLimit(aUpperLimit)
, mLowerLimit(aLowerLimit)
, mSpaceType(aSpaceType) { }
NS_IMETHOD Run()
{
FMRadioSettings info;
info.upperLimit() = mUpperLimit;
info.lowerLimit() = mLowerLimit;
info.spaceType() = mSpaceType;
EnableFMRadio(info);
nsCOMPtr<nsIAudioManager> audioManager =
do_GetService(NS_AUDIOMANAGER_CONTRACTID);
audioManager->SetFmRadioAudioEnabled(true);
// TODO apply path from bug 862899: AudioChannelAgent per process
return NS_OK;
}
private:
int32_t mUpperLimit;
int32_t mLowerLimit;
int32_t mSpaceType;
};
/**
* Read the airplane-mode setting, if the airplane-mode is not enabled, we
* enable the FM radio.
*/
class ReadRilSettingTask MOZ_FINAL : public nsISettingsServiceCallback
{
public:
NS_DECL_ISUPPORTS
ReadRilSettingTask(nsRefPtr<ReplyRunnable> aPendingRequest)
: mPendingRequest(aPendingRequest) { }
NS_IMETHOD
Handle(const nsAString& aName, const JS::Value& aResult)
{
FMRadioService* fmRadioService = FMRadioService::Singleton();
MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
fmRadioService->mHasReadRilSetting = true;
if (!aResult.isBoolean()) {
// Failed to read the setting value, set the state back to Disabled.
fmRadioService->TransitionState(
ErrorResponse(NS_LITERAL_STRING("Unexpected error")), Disabled);
return NS_OK;
}
fmRadioService->mRilDisabled = aResult.toBoolean();
if (!fmRadioService->mRilDisabled) {
EnableRunnable* runnable =
new EnableRunnable(fmRadioService->mUpperBoundInKHz,
fmRadioService->mLowerBoundInKHz,
fmRadioService->mChannelWidthInKHz);
NS_DispatchToMainThread(runnable);
} else {
// Airplane mode is enabled, set the state back to Disabled.
fmRadioService->TransitionState(ErrorResponse(
NS_LITERAL_STRING("Airplane mode currently enabled")), Disabled);
}
return NS_OK;
}
NS_IMETHOD
HandleError(const nsAString& aName)
{
FMRadioService* fmRadioService = FMRadioService::Singleton();
MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
fmRadioService->TransitionState(ErrorResponse(
NS_LITERAL_STRING("Unexpected error")), Disabled);
return NS_OK;
}
private:
nsRefPtr<ReplyRunnable> mPendingRequest;
};
NS_IMPL_ISUPPORTS1(ReadRilSettingTask, nsISettingsServiceCallback)
class DisableRunnable MOZ_FINAL : public nsRunnable
{
public:
DisableRunnable() { }
NS_IMETHOD Run()
{
// Fix Bug 796733. DisableFMRadio should be called before
// SetFmRadioAudioEnabled to prevent the annoying beep sound.
DisableFMRadio();
nsCOMPtr<nsIAudioManager> audioManager =
do_GetService(NS_AUDIOMANAGER_CONTRACTID);
audioManager->SetFmRadioAudioEnabled(false);
return NS_OK;
}
};
class SetFrequencyRunnable MOZ_FINAL : public nsRunnable
{
public:
SetFrequencyRunnable(int32_t aFrequency)
: mFrequency(aFrequency) { }
NS_IMETHOD Run()
{
SetFMRadioFrequency(mFrequency);
FMRadioService* fmRadioService = FMRadioService::Singleton();
fmRadioService->UpdateFrequency();
return NS_OK;
}
private:
int32_t mFrequency;
};
class SeekRunnable MOZ_FINAL : public nsRunnable
{
public:
SeekRunnable(FMRadioSeekDirection aDirection) : mDirection(aDirection) { }
NS_IMETHOD Run()
{
switch (mDirection) {
case FM_RADIO_SEEK_DIRECTION_UP:
case FM_RADIO_SEEK_DIRECTION_DOWN:
FMRadioSeek(mDirection);
break;
default:
MOZ_CRASH();
}
return NS_OK;
}
private:
FMRadioSeekDirection mDirection;
};
void
FMRadioService::TransitionState(const FMRadioResponseType& aResponse,
FMRadioState aState)
{
if (mPendingRequest) {
mPendingRequest->SetReply(aResponse);
NS_DispatchToMainThread(mPendingRequest);
}
SetState(aState);
}
void
FMRadioService::SetState(FMRadioState aState)
{
mState = aState;
mPendingRequest = nullptr;
}
void
FMRadioService::AddObserver(FMRadioEventObserver* aObserver)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
mObserverList.AddObserver(aObserver);
}
void
FMRadioService::RemoveObserver(FMRadioEventObserver* aObserver)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
mObserverList.RemoveObserver(aObserver);
if (mObserverList.Length() == 0)
{
// Turning off the FM radio HW because observer list is empty.
if (IsFMRadioOn()) {
NS_DispatchToMainThread(new DisableRunnable());
}
}
}
/**
* Round the frequency to match the range of frequency and the channel width. If
* the given frequency is out of range, return 0. For example:
* - lower: 87500KHz, upper: 108000KHz, channel width: 200KHz
* 87.6MHz is rounded to 87700KHz
* 87.58MHz is rounded to 87500KHz
* 87.49MHz is rounded to 87500KHz
* 109MHz is not rounded, 0 will be returned
*
* We take frequency in MHz to prevent precision losing, and return rounded
* value in KHz for Gonk using.
*/
int32_t
FMRadioService::RoundFrequency(double aFrequencyInMHz)
{
double halfChannelWidthInMHz = mChannelWidthInKHz / 1000.0 / 2;
// Make sure 87.49999MHz would be rounded to the lower bound when
// the lower bound is 87500KHz.
if (aFrequencyInMHz < mLowerBoundInKHz / 1000.0 - halfChannelWidthInMHz ||
aFrequencyInMHz > mUpperBoundInKHz / 1000.0 + halfChannelWidthInMHz) {
return 0;
}
int32_t partToBeRounded = round(aFrequencyInMHz * 1000) - mLowerBoundInKHz;
int32_t roundedPart = round(partToBeRounded / (double)mChannelWidthInKHz) *
mChannelWidthInKHz;
return mLowerBoundInKHz + roundedPart;
}
bool
FMRadioService::IsEnabled() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
return IsFMRadioOn();
}
double
FMRadioService::GetFrequency() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
if (IsEnabled()) {
int32_t frequencyInKHz = GetFMRadioFrequency();
return frequencyInKHz / 1000.0;
}
return 0;
}
double
FMRadioService::GetFrequencyUpperBound() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
return mUpperBoundInKHz / 1000.0;
}
double
FMRadioService::GetFrequencyLowerBound() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
return mLowerBoundInKHz / 1000.0;
}
double
FMRadioService::GetChannelWidth() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
return mChannelWidthInKHz / 1000.0;
}
void
FMRadioService::Enable(double aFrequencyInMHz, ReplyRunnable* aReplyRunnable)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aReplyRunnable);
switch (mState) {
case Seeking:
case Enabled:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently enabled")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Disabling:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Enabling:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Disabled:
break;
}
int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
if (!roundedFrequency) {
aReplyRunnable->SetReply(ErrorResponse(
NS_LITERAL_STRING("Frequency is out of range")));
NS_DispatchToMainThread(aReplyRunnable);
return;
}
if (mHasReadRilSetting && mRilDisabled) {
aReplyRunnable->SetReply(ErrorResponse(
NS_LITERAL_STRING("Airplane mode currently enabled")));
NS_DispatchToMainThread(aReplyRunnable);
return;
}
SetState(Enabling);
// Cache the enable request just in case disable() is called
// while the FM radio HW is being enabled.
mPendingRequest = aReplyRunnable;
// Cache the frequency value, and set it after the FM radio HW is enabled
mPendingFrequencyInKHz = roundedFrequency;
if (!mHasReadRilSetting) {
nsCOMPtr<nsISettingsService> settings =
do_GetService("@mozilla.org/settingsService;1");
nsCOMPtr<nsISettingsServiceLock> settingsLock;
nsresult rv = settings->CreateLock(getter_AddRefs(settingsLock));
if (NS_FAILED(rv)) {
TransitionState(ErrorResponse(
NS_LITERAL_STRING("Can't create settings lock")), Disabled);
return;
}
nsRefPtr<ReadRilSettingTask> callback =
new ReadRilSettingTask(mPendingRequest);
rv = settingsLock->Get(SETTING_KEY_RIL_RADIO_DISABLED, callback);
if (NS_FAILED(rv)) {
TransitionState(ErrorResponse(
NS_LITERAL_STRING("Can't get settings lock")), Disabled);
}
return;
}
NS_DispatchToMainThread(new EnableRunnable(mUpperBoundInKHz,
mLowerBoundInKHz,
mChannelWidthInKHz));
}
void
FMRadioService::Disable(ReplyRunnable* aReplyRunnable)
{
// When airplane-mode is enabled, we will call this function from
// FMRadioService::Observe without passing a ReplyRunnable, so we have to
// check if |aReplyRunnable| is null before we dispatch it.
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
switch (mState) {
case Disabling:
if (aReplyRunnable) {
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
NS_DispatchToMainThread(aReplyRunnable);
}
return;
case Disabled:
if (aReplyRunnable) {
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
NS_DispatchToMainThread(aReplyRunnable);
}
return;
case Enabled:
case Enabling:
case Seeking:
break;
}
nsRefPtr<ReplyRunnable> enablingRequest = mPendingRequest;
// If the FM Radio is currently seeking, no fail-to-seek or similar
// event will be fired, execute the seek callback manually.
if (mState == Seeking) {
TransitionState(ErrorResponse(
NS_LITERAL_STRING("Seek action is cancelled")), Disabling);
}
FMRadioState preState = mState;
SetState(Disabling);
mPendingRequest = aReplyRunnable;
if (preState == Enabling) {
// If the radio is currently enabling, we fire the error callback on the
// enable request immediately. When the radio finishes enabling, we'll call
// DoDisable and fire the success callback on the disable request.
enablingRequest->SetReply(
ErrorResponse(NS_LITERAL_STRING("Enable action is cancelled")));
NS_DispatchToMainThread(enablingRequest);
// If we haven't read the ril settings yet we won't enable the FM radio HW,
// so fail the disable request immediately.
if (!mHasReadRilSetting) {
SetState(Disabled);
if (aReplyRunnable) {
aReplyRunnable->SetReply(SuccessResponse());
NS_DispatchToMainThread(aReplyRunnable);
}
}
return;
}
DoDisable();
}
void
FMRadioService::DoDisable()
{
// To make such codes work:
// navigator.mozFMRadio.disable();
// navigator.mozFMRadio.ondisabled = function() {
// console.log("We will catch disabled event ");
// };
// we need to call hal::DisableFMRadio() asynchronously. Same reason for
// EnableRunnable and SetFrequencyRunnable.
NS_DispatchToMainThread(new DisableRunnable());
}
void
FMRadioService::SetFrequency(double aFrequencyInMHz,
ReplyRunnable* aReplyRunnable)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aReplyRunnable);
switch (mState) {
case Disabled:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Enabling:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Disabling:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Seeking:
CancelFMRadioSeek();
TransitionState(ErrorResponse(
NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
break;
case Enabled:
break;
}
int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
if (!roundedFrequency) {
aReplyRunnable->SetReply(ErrorResponse(
NS_LITERAL_STRING("Frequency is out of range")));
NS_DispatchToMainThread(aReplyRunnable);
return;
}
NS_DispatchToMainThread(new SetFrequencyRunnable(roundedFrequency));
aReplyRunnable->SetReply(SuccessResponse());
NS_DispatchToMainThread(aReplyRunnable);
}
void
FMRadioService::Seek(FMRadioSeekDirection aDirection,
ReplyRunnable* aReplyRunnable)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aReplyRunnable);
switch (mState) {
case Enabling:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Disabled:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Seeking:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently seeking")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Disabling:
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
NS_DispatchToMainThread(aReplyRunnable);
return;
case Enabled:
break;
}
SetState(Seeking);
mPendingRequest = aReplyRunnable;
NS_DispatchToMainThread(new SeekRunnable(aDirection));
}
void
FMRadioService::CancelSeek(ReplyRunnable* aReplyRunnable)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aReplyRunnable);
// We accept canceling seek request only if it's currently seeking.
if (mState != Seeking) {
aReplyRunnable->SetReply(
ErrorResponse(NS_LITERAL_STRING("FM radio currently not seeking")));
NS_DispatchToMainThread(aReplyRunnable);
return;
}
// Cancel the seek immediately to prevent it from completing.
CancelFMRadioSeek();
TransitionState(
ErrorResponse(NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
aReplyRunnable->SetReply(SuccessResponse());
NS_DispatchToMainThread(aReplyRunnable);
}
NS_IMETHODIMP
FMRadioService::Observe(nsISupports * aSubject,
const char * aTopic,
const PRUnichar * aData)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sFMRadioService);
if (strcmp(aTopic, MOZSETTINGS_CHANGED_ID) != 0) {
return NS_OK;
}
// The string that we're interested in will be a JSON string looks like:
// {"key":"ril.radio.disabled","value":true}
AutoSafeJSContext cx;
const nsDependentString dataStr(aData);
JS::Rooted<JS::Value> val(cx);
if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
!val.isObject()) {
return NS_OK;
}
JSObject& obj(val.toObject());
JS::Rooted<JS::Value> key(cx);
if (!JS_GetProperty(cx, &obj, "key", &key) ||
!key.isString()) {
return NS_OK;
}
JS::Rooted<JSString*> jsKey(cx, key.toString());
nsDependentJSString keyStr;
if (!keyStr.init(cx, jsKey)) {
return NS_OK;
}
JS::Rooted<JS::Value> value(cx);
if (!JS_GetProperty(cx, &obj, "value", &value)) {
return NS_OK;
}
if (keyStr.EqualsLiteral(SETTING_KEY_RIL_RADIO_DISABLED)) {
if (!value.isBoolean()) {
return NS_OK;
}
mRilDisabled = value.toBoolean();
mHasReadRilSetting = true;
// Disable the FM radio HW if Airplane mode is enabled.
if (mRilDisabled) {
Disable(nullptr);
}
return NS_OK;
}
return NS_OK;
}
void
FMRadioService::NotifyFMRadioEvent(FMRadioEventType aType)
{
mObserverList.Broadcast(aType);
}
void
FMRadioService::Notify(const FMRadioOperationInformation& aInfo)
{
switch (aInfo.operation()) {
case FM_RADIO_OPERATION_ENABLE:
MOZ_ASSERT(IsFMRadioOn());
MOZ_ASSERT(mState == Disabling || mState == Enabling);
// If we're disabling, disable the radio right now.
if (mState == Disabling) {
DoDisable();
return;
}
// Fire success callback on the enable request.
TransitionState(SuccessResponse(), Enabled);
// To make sure the FM app will get the right frequency after the FM
// radio is enabled, we have to set the frequency first.
SetFMRadioFrequency(mPendingFrequencyInKHz);
// Update the current frequency without sending the`FrequencyChanged`
// event, to make sure the FM app will get the right frequency when the
// `EnabledChange` event is sent.
mPendingFrequencyInKHz = GetFMRadioFrequency();
UpdatePowerState();
// The frequency was changed from '0' to some meaningful number, so we
// should send the `FrequencyChanged` event manually.
NotifyFMRadioEvent(FrequencyChanged);
break;
case FM_RADIO_OPERATION_DISABLE:
MOZ_ASSERT(mState == Disabling);
TransitionState(SuccessResponse(), Disabled);
UpdatePowerState();
break;
case FM_RADIO_OPERATION_SEEK:
// Seek action might be cancelled by SetFrequency(), we need to check if
// the current state is Seeking.
if (mState == Seeking) {
TransitionState(SuccessResponse(), Enabled);
}
UpdateFrequency();
break;
default:
MOZ_CRASH();
}
}
void
FMRadioService::UpdatePowerState()
{
bool enabled = IsFMRadioOn();
if (enabled != mEnabled) {
mEnabled = enabled;
NotifyFMRadioEvent(EnabledChanged);
}
}
void
FMRadioService::UpdateFrequency()
{
int32_t frequency = GetFMRadioFrequency();
if (mPendingFrequencyInKHz != frequency) {
mPendingFrequencyInKHz = frequency;
NotifyFMRadioEvent(FrequencyChanged);
}
}
// static
FMRadioService*
FMRadioService::Singleton()
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
MOZ_ASSERT(NS_IsMainThread());
if (!sFMRadioService) {
sFMRadioService = new FMRadioService();
}
return sFMRadioService;
}
NS_IMPL_ISUPPORTS1(FMRadioService, nsIObserver)
END_FMRADIO_NAMESPACE

View File

@ -0,0 +1,207 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_fmradioservice_h__
#define mozilla_dom_fmradioservice_h__
#include "mozilla/dom/PFMRadioRequest.h"
#include "FMRadioCommon.h"
#include "mozilla/Hal.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Services.h"
#include "nsThreadUtils.h"
#include "nsIObserver.h"
#include "nsXULAppAPI.h"
BEGIN_FMRADIO_NAMESPACE
class ReplyRunnable : public nsRunnable
{
public:
ReplyRunnable() : mResponseType(SuccessResponse()) {}
virtual ~ReplyRunnable() {}
void
SetReply(const FMRadioResponseType& aResponseType)
{
mResponseType = aResponseType;
}
protected:
FMRadioResponseType mResponseType;
};
/**
* The FMRadio Service Interface for FMRadio.
*
* There are two concrete classes which implement this interface:
* - FMRadioService
* It's used in the main process, implements all the logics about FM Radio.
*
* - FMRadioChild
* It's used in subprocess. It's a kind of proxy which just sends all
* the requests to main process through IPC channel.
*
* All the requests coming from the content page will be redirected to the
* concrete class object.
*
* Consider navigator.mozFMRadio.enable(). Here is the call sequence:
* - OOP
* Child:
* (1) Call navigator.mozFMRadio.enable().
* (2) Return a DOMRequest object, and call FMRadioChild.Enable() with a
* ReplyRunnable object.
* (3) Send IPC message to main process.
* Parent:
* (4) Call FMRadioService::Enable() with a ReplyRunnable object.
* (5) Call hal::EnableFMRadio().
* (6) Notify FMRadioService object when FM radio HW is enabled.
* (7) Dispatch the ReplyRunnable object created in (4).
* (8) Send IPC message back to child process.
* Child:
* (9) Dispatch the ReplyRunnable object created in (2).
* (10) Fire success callback of the DOMRequest Object created in (2).
* _ _ _ _ _ _ _ _ _ _ _ _ _ _
* | OOP |
* | |
* Page FMRadio | FMRadioChild IPC | FMRadioService Hal
* | (1) | | | | | | |
* |----->| (2) | | | | | |
* | |--------|--------->| (3) | | | |
* | | | |-----------> | | (4) | |
* | | | | |--|---------->| (5) |
* | | | | | | |--------->|
* | | | | | | | (6) |
* | | | | | | (7) |<---------|
* | | | | (8) |<-|-----------| |
* | | (9) | |<----------- | | | |
* | (10) |<-------|----------| | | | |
* |<-----| | | | | | |
* | |
* |_ _ _ _ _ _ _ _ _ _ _ _ _ _|
* - non-OOP
* In non-OOP model, we don't need to send messages between processes, so
* the call sequences are much more simpler, it almost just follows the
* sequences presented in OOP model: (1) (2) (5) (6) (9) and (10).
*
*/
class IFMRadioService
{
protected:
virtual ~IFMRadioService() { }
public:
virtual bool IsEnabled() const = 0;
virtual double GetFrequency() const = 0;
virtual double GetFrequencyUpperBound() const = 0;
virtual double GetFrequencyLowerBound() const = 0;
virtual double GetChannelWidth() const = 0;
virtual void Enable(double aFrequency, ReplyRunnable* aReplyRunnable) = 0;
virtual void Disable(ReplyRunnable* aReplyRunnable) = 0;
virtual void SetFrequency(double aFrequency, ReplyRunnable* aReplyRunnable) = 0;
virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
ReplyRunnable* aReplyRunnable) = 0;
virtual void CancelSeek(ReplyRunnable* aReplyRunnable) = 0;
/**
* Register handler to receive the FM Radio events, including:
* - StateChangedEvent
* - FrequencyChangedEvent
*
* Called by FMRadio and FMRadioParent.
*/
virtual void AddObserver(FMRadioEventObserver* aObserver) = 0;
virtual void RemoveObserver(FMRadioEventObserver* aObserver) = 0;
/**
* Static method to return the singleton instance. If it's in the child
* process, we will get an object of FMRadioChild.
*/
static IFMRadioService* Singleton();
};
enum FMRadioState
{
Disabled,
Disabling,
Enabling,
Enabled,
Seeking
};
class FMRadioService MOZ_FINAL : public IFMRadioService
, public hal::FMRadioObserver
, public nsIObserver
{
friend class ReadRilSettingTask;
friend class SetFrequencyRunnable;
public:
static FMRadioService* Singleton();
virtual ~FMRadioService();
NS_DECL_ISUPPORTS
virtual bool IsEnabled() const MOZ_OVERRIDE;
virtual double GetFrequency() const MOZ_OVERRIDE;
virtual double GetFrequencyUpperBound() const MOZ_OVERRIDE;
virtual double GetFrequencyLowerBound() const MOZ_OVERRIDE;
virtual double GetChannelWidth() const MOZ_OVERRIDE;
virtual void Enable(double aFrequency, ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void Disable(ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void SetFrequency(double aFrequency, ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void CancelSeek(ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void AddObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
virtual void RemoveObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
/* FMRadioObserver */
void Notify(const hal::FMRadioOperationInformation& aInfo) MOZ_OVERRIDE;
NS_DECL_NSIOBSERVER
protected:
FMRadioService();
private:
int32_t RoundFrequency(double aFrequencyInMHz);
void NotifyFMRadioEvent(FMRadioEventType aType);
void DoDisable();
void TransitionState(const FMRadioResponseType& aResponse, FMRadioState aState);
void SetState(FMRadioState aState);
void UpdatePowerState();
void UpdateFrequency();
private:
bool mEnabled;
int32_t mPendingFrequencyInKHz;
FMRadioState mState;
bool mHasReadRilSetting;
bool mRilDisabled;
double mUpperBoundInKHz;
double mLowerBoundInKHz;
double mChannelWidthInKHz;
nsRefPtr<ReplyRunnable> mPendingRequest;
FMRadioEventObserverList mObserverList;
static StaticRefPtr<FMRadioService> sFMRadioService;
};
END_FMRADIO_NAMESPACE
#endif // mozilla_dom_fmradioservice_h__

View File

@ -2,7 +2,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
@ -13,3 +13,4 @@ include $(topsrcdir)/dom/dom-config.mk
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk

View File

@ -0,0 +1,182 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FMRadioChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/FMRadioRequestChild.h"
using namespace mozilla::hal;
BEGIN_FMRADIO_NAMESPACE
StaticAutoPtr<FMRadioChild> FMRadioChild::sFMRadioChild;
FMRadioChild::FMRadioChild()
: mEnabled(false)
, mFrequency(0)
, mObserverList(FMRadioEventObserverList())
{
MOZ_COUNT_CTOR(FMRadioChild);
ContentChild::GetSingleton()->SendPFMRadioConstructor(this);
StatusInfo statusInfo;
SendGetStatusInfo(&statusInfo);
mEnabled = statusInfo.enabled();
mFrequency = statusInfo.frequency();
mUpperBound = statusInfo.upperBound();
mLowerBound= statusInfo.lowerBound();
mChannelWidth = statusInfo.channelWidth();
}
FMRadioChild::~FMRadioChild()
{
MOZ_COUNT_DTOR(FMRadioChild);
}
bool
FMRadioChild::IsEnabled() const
{
return mEnabled;
}
double
FMRadioChild::GetFrequency() const
{
return mFrequency;
}
double
FMRadioChild::GetFrequencyUpperBound() const
{
return mUpperBound;
}
double
FMRadioChild::GetFrequencyLowerBound() const
{
return mLowerBound;
}
double
FMRadioChild::GetChannelWidth() const
{
return mChannelWidth;
}
void
FMRadioChild::Enable(double aFrequency, ReplyRunnable* aReplyRunnable)
{
SendRequest(aReplyRunnable, EnableRequestArgs(aFrequency));
}
void
FMRadioChild::Disable(ReplyRunnable* aReplyRunnable)
{
SendRequest(aReplyRunnable, DisableRequestArgs());
}
void
FMRadioChild::SetFrequency(double aFrequency,
ReplyRunnable* aReplyRunnable)
{
SendRequest(aReplyRunnable, SetFrequencyRequestArgs(aFrequency));
}
void
FMRadioChild::Seek(FMRadioSeekDirection aDirection, ReplyRunnable* aReplyRunnable)
{
SendRequest(aReplyRunnable, SeekRequestArgs(aDirection));
}
void
FMRadioChild::CancelSeek(ReplyRunnable* aReplyRunnable)
{
SendRequest(aReplyRunnable, CancelSeekRequestArgs());
}
inline void
FMRadioChild::NotifyFMRadioEvent(FMRadioEventType aType)
{
mObserverList.Broadcast(aType);
}
void
FMRadioChild::AddObserver(FMRadioEventObserver* aObserver)
{
mObserverList.AddObserver(aObserver);
}
void
FMRadioChild::RemoveObserver(FMRadioEventObserver* aObserver)
{
mObserverList.RemoveObserver(aObserver);
}
void
FMRadioChild::SendRequest(ReplyRunnable* aReplyRunnable,
FMRadioRequestArgs aArgs)
{
PFMRadioRequestChild* childRequest = new FMRadioRequestChild(aReplyRunnable);
SendPFMRadioRequestConstructor(childRequest, aArgs);
}
bool
FMRadioChild::RecvNotifyFrequencyChanged(const double& aFrequency)
{
mFrequency = aFrequency;
NotifyFMRadioEvent(FrequencyChanged);
return true;
}
bool
FMRadioChild::RecvNotifyEnabledChanged(const bool& aEnabled,
const double& aFrequency)
{
mEnabled = aEnabled;
mFrequency = aFrequency;
NotifyFMRadioEvent(EnabledChanged);
return true;
}
bool
FMRadioChild::Recv__delete__()
{
return true;
}
PFMRadioRequestChild*
FMRadioChild::AllocPFMRadioRequestChild(const FMRadioRequestArgs& aArgs)
{
MOZ_CRASH();
return nullptr;
}
bool
FMRadioChild::DeallocPFMRadioRequestChild(PFMRadioRequestChild* aActor)
{
delete aActor;
return true;
}
// static
FMRadioChild*
FMRadioChild::Singleton()
{
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
MOZ_ASSERT(NS_IsMainThread());
if (!sFMRadioChild) {
sFMRadioChild = new FMRadioChild();
}
return sFMRadioChild;
}
END_FMRADIO_NAMESPACE

View File

@ -0,0 +1,91 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_fmradiochild_h__
#define mozilla_dom_fmradiochild_h__
#include "FMRadioCommon.h"
#include "FMRadioService.h"
#include "mozilla/dom/PFMRadioChild.h"
#include "mozilla/StaticPtr.h"
BEGIN_FMRADIO_NAMESPACE
/**
* FMRadioChild plays two roles:
* - Kind of proxy of FMRadioService
* Redirect all the requests coming from web content to FMRadioService
* in parent through IPC channel.
* - Child Actor of PFMRadio
* IPC channel to transfer the requests.
*/
class FMRadioChild MOZ_FINAL : public IFMRadioService
, public PFMRadioChild
{
public:
static FMRadioChild* Singleton();
~FMRadioChild();
void SendRequest(ReplyRunnable* aReplyRunnable, FMRadioRequestArgs aArgs);
/* IFMRadioService */
virtual bool IsEnabled() const MOZ_OVERRIDE;
virtual double GetFrequency() const MOZ_OVERRIDE;
virtual double GetFrequencyUpperBound() const MOZ_OVERRIDE;
virtual double GetFrequencyLowerBound() const MOZ_OVERRIDE;
virtual double GetChannelWidth() const MOZ_OVERRIDE;
virtual void Enable(double aFrequency, ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void Disable(ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void SetFrequency(double frequency,
ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void CancelSeek(ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
virtual void AddObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
virtual void RemoveObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
/* PFMRadioChild */
virtual bool
Recv__delete__() MOZ_OVERRIDE;
virtual bool
RecvNotifyFrequencyChanged(const double& aFrequency) MOZ_OVERRIDE;
virtual bool
RecvNotifyEnabledChanged(const bool& aEnabled,
const double& aFrequency) MOZ_OVERRIDE;
virtual PFMRadioRequestChild*
AllocPFMRadioRequestChild(const FMRadioRequestArgs& aArgs) MOZ_OVERRIDE;
virtual bool
DeallocPFMRadioRequestChild(PFMRadioRequestChild* aActor) MOZ_OVERRIDE;
private:
FMRadioChild();
void Init();
inline void NotifyFMRadioEvent(FMRadioEventType aType);
bool mEnabled;
double mFrequency;
double mUpperBound;
double mLowerBound;
double mChannelWidth;
FMRadioEventObserverList mObserverList;
private:
static StaticAutoPtr<FMRadioChild> sFMRadioChild;
};
END_FMRADIO_NAMESPACE
#endif // mozilla_dom_fmradiochild_h__

View File

@ -0,0 +1,101 @@
/* -*- Mode: C++; 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/. */
#include "FMRadioParent.h"
#include "mozilla/unused.h"
#include "mozilla/dom/ContentParent.h"
#include "FMRadioRequestParent.h"
#include "FMRadioService.h"
BEGIN_FMRADIO_NAMESPACE
FMRadioParent::FMRadioParent()
{
MOZ_COUNT_CTOR(FMRadioParent);
IFMRadioService::Singleton()->AddObserver(this);
}
FMRadioParent::~FMRadioParent()
{
MOZ_COUNT_DTOR(FMRadioParent);
IFMRadioService::Singleton()->RemoveObserver(this);
}
bool
FMRadioParent::RecvGetStatusInfo(StatusInfo* aStatusInfo)
{
aStatusInfo->enabled() = IFMRadioService::Singleton()->IsEnabled();
aStatusInfo->frequency() = IFMRadioService::Singleton()->GetFrequency();
aStatusInfo->upperBound() =
IFMRadioService::Singleton()->GetFrequencyUpperBound();
aStatusInfo->lowerBound() =
IFMRadioService::Singleton()->GetFrequencyLowerBound();
aStatusInfo->channelWidth() =
IFMRadioService::Singleton()->GetChannelWidth();
return true;
}
PFMRadioRequestParent*
FMRadioParent::AllocPFMRadioRequestParent(const FMRadioRequestArgs& aArgs)
{
nsRefPtr<FMRadioRequestParent> requestParent = new FMRadioRequestParent();
switch (aArgs.type()) {
case FMRadioRequestArgs::TEnableRequestArgs:
IFMRadioService::Singleton()->Enable(
aArgs.get_EnableRequestArgs().frequency(), requestParent);
break;
case FMRadioRequestArgs::TDisableRequestArgs:
IFMRadioService::Singleton()->Disable(requestParent);
break;
case FMRadioRequestArgs::TSetFrequencyRequestArgs:
IFMRadioService::Singleton()->SetFrequency(
aArgs.get_SetFrequencyRequestArgs().frequency(), requestParent);
break;
case FMRadioRequestArgs::TSeekRequestArgs:
IFMRadioService::Singleton()->Seek(
aArgs.get_SeekRequestArgs().direction(), requestParent);
break;
case FMRadioRequestArgs::TCancelSeekRequestArgs:
IFMRadioService::Singleton()->CancelSeek(requestParent);
break;
default:
MOZ_CRASH();
}
return requestParent.forget().get();
}
bool
FMRadioParent::DeallocPFMRadioRequestParent(PFMRadioRequestParent* aActor)
{
FMRadioRequestParent* parent = static_cast<FMRadioRequestParent*>(aActor);
NS_RELEASE(parent);
return true;
}
void
FMRadioParent::Notify(const FMRadioEventType& aType)
{
switch (aType) {
case FrequencyChanged:
unused << SendNotifyFrequencyChanged(
IFMRadioService::Singleton()->GetFrequency());
break;
case EnabledChanged:
unused << SendNotifyEnabledChanged(
IFMRadioService::Singleton()->IsEnabled(),
IFMRadioService::Singleton()->GetFrequency());
break;
default:
NS_RUNTIMEABORT("not reached");
break;
}
}
END_FMRADIO_NAMESPACE

View File

@ -0,0 +1,41 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_fmradioparent_h__
#define mozilla_dom_fmradioparent_h__
#include "FMRadioCommon.h"
#include "mozilla/dom/PFMRadioParent.h"
#include "mozilla/HalTypes.h"
BEGIN_FMRADIO_NAMESPACE
class PFMRadioRequestParent;
class FMRadioParent MOZ_FINAL : public PFMRadioParent
, public FMRadioEventObserver
{
public:
FMRadioParent();
~FMRadioParent();
virtual bool
RecvGetStatusInfo(StatusInfo* aStatusInfo) MOZ_OVERRIDE;
virtual PFMRadioRequestParent*
AllocPFMRadioRequestParent(const FMRadioRequestArgs& aArgs) MOZ_OVERRIDE;
virtual bool
DeallocPFMRadioRequestParent(PFMRadioRequestParent* aActor) MOZ_OVERRIDE;
/* FMRadioEventObserver */
virtual void Notify(const FMRadioEventType& aType) MOZ_OVERRIDE;
};
END_FMRADIO_NAMESPACE
#endif // mozilla_dom_fmradioparent_h__

View File

@ -0,0 +1,33 @@
/* -*- Mode: C++; 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/. */
#include "mozilla/dom/PFMRadioRequestChild.h"
#include "FMRadioRequestChild.h"
#include "FMRadioService.h"
BEGIN_FMRADIO_NAMESPACE
FMRadioRequestChild::FMRadioRequestChild(ReplyRunnable* aReplyRunnable)
: mReplyRunnable(aReplyRunnable)
{
MOZ_COUNT_CTOR(FMRadioRequestChild);
}
FMRadioRequestChild::~FMRadioRequestChild()
{
MOZ_COUNT_DTOR(FMRadioRequestChild);
}
bool
FMRadioRequestChild::Recv__delete__(const FMRadioResponseType& aType)
{
mReplyRunnable->SetReply(aType);
NS_DispatchToMainThread(mReplyRunnable);
return true;
}
END_FMRADIO_NAMESPACE

View File

@ -0,0 +1,34 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_fmradiorequestchild_h__
#define mozilla_dom_fmradiorequestchild_h__
#include "FMRadioCommon.h"
#include "mozilla/dom/PFMRadioRequestChild.h"
#include "DOMRequest.h"
BEGIN_FMRADIO_NAMESPACE
class ReplyRunnable;
class FMRadioRequestChild MOZ_FINAL : public PFMRadioRequestChild
{
public:
FMRadioRequestChild(ReplyRunnable* aReplyRunnable);
~FMRadioRequestChild();
virtual bool
Recv__delete__(const FMRadioResponseType& aResponse) MOZ_OVERRIDE;
private:
nsRefPtr<ReplyRunnable> mReplyRunnable;
};
END_FMRADIO_NAMESPACE
#endif // mozilla_dom_fmradiorequestchild_h__

View File

@ -0,0 +1,43 @@
/* -*- Mode: C++; 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/. */
#include "FMRadioRequestParent.h"
#include "FMRadioService.h"
#include "mozilla/unused.h"
#include "mozilla/dom/PFMRadio.h"
BEGIN_FMRADIO_NAMESPACE
FMRadioRequestParent::FMRadioRequestParent()
: mActorDestroyed(false)
{
MOZ_COUNT_CTOR(FMRadioRequestParent);
}
FMRadioRequestParent::~FMRadioRequestParent()
{
MOZ_COUNT_DTOR(FMRadioRequestParent);
}
void
FMRadioRequestParent::ActorDestroy(ActorDestroyReason aWhy)
{
mActorDestroyed = true;
}
nsresult
FMRadioRequestParent::Run()
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
if (!mActorDestroyed) {
unused << Send__delete__(this, mResponseType);
}
return NS_OK;
}
END_FMRADIO_NAMESPACE

View File

@ -0,0 +1,34 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_fmradiorequestparent_h__
#define mozilla_dom_fmradiorequestparent_h__
#include "FMRadioCommon.h"
#include "mozilla/dom/PFMRadioRequestParent.h"
#include "FMRadioService.h"
BEGIN_FMRADIO_NAMESPACE
class FMRadioRequestParent MOZ_FINAL : public PFMRadioRequestParent
, public ReplyRunnable
{
public:
FMRadioRequestParent();
~FMRadioRequestParent();
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
NS_IMETHOD Run();
private:
bool mActorDestroyed;
};
END_FMRADIO_NAMESPACE
#endif // mozilla_dom_fmradiorequestparent_h__

View File

@ -0,0 +1,20 @@
# 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/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
LOCAL_INCLUDES += \
-I$(topsrcdir)/dom/fmradio \
$(NULL)
include $(topsrcdir)/dom/dom-config.mk
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk

View File

@ -0,0 +1,94 @@
/* 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 "mozilla/HalTypes.h";
include protocol PContent;
include protocol PFMRadioRequest;
using mozilla::hal::FMRadioSeekDirection;
namespace mozilla {
namespace dom {
struct EnableRequestArgs
{
double frequency;
};
struct DisableRequestArgs
{
};
struct SetFrequencyRequestArgs
{
double frequency;
};
struct SeekRequestArgs
{
FMRadioSeekDirection direction;
};
struct CancelSeekRequestArgs
{
};
union FMRadioRequestArgs
{
EnableRequestArgs;
DisableRequestArgs;
SetFrequencyRequestArgs;
SeekRequestArgs;
CancelSeekRequestArgs;
};
struct StatusInfo
{
bool enabled;
double frequency;
double upperBound;
double lowerBound;
double channelWidth;
};
sync protocol PFMRadio
{
manager PContent;
manages PFMRadioRequest;
child:
/**
* Sent when the frequency is changed.
*/
NotifyFrequencyChanged(double frequency);
/**
* Sent when the power state of FM radio HW is changed.
*/
NotifyEnabledChanged(bool enabled, double frequency);
__delete__();
parent:
/**
* Get the current status infomation of FM radio HW synchronously.
* Sent when the singleton object of FMRadioChild is initialized.
*/
sync GetStatusInfo() returns (StatusInfo info);
/**
* Send request to parent process to operate the FM radio HW.
*
* We don't have separate Enable/SetFrequency/etc. methods instead here,
* because we can leverage the IPC messaging mechanism to manage the mapping
* of the asynchronous request and the DOMRequest we returned to the caller
* on web content, otherwise, we have to do the mapping stuff manually which
* is more error prone.
*/
PFMRadioRequest(FMRadioRequestArgs requestType);
};
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,43 @@
/* 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 protocol PFMRadio;
namespace mozilla {
namespace dom {
struct ErrorResponse
{
nsString error;
};
struct SuccessResponse
{
};
union FMRadioResponseType
{
ErrorResponse;
SuccessResponse;
};
/**
* The protocol is used for sending asynchronous operation requests of
* FM radio HW from child to parent, and the type of the request is defined in
* FMRadioRequestArgs.
*
* When the request completed, the result, i.e. FMRadioResponseType, will be
* sent back to child from parent in the `__delete__` message.
*/
async protocol PFMRadioRequest
{
manager PFMRadio;
child:
__delete__(FMRadioResponseType response);
};
} // namespace dom
} // namespace mozilla

View File

@ -4,28 +4,22 @@
# 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/.
XPIDL_SOURCES += [
'nsIDOMFMRadio.idl',
'nsIFMRadio.idl',
EXPORTS.mozilla.dom += [
'FMRadioChild.h',
'FMRadioParent.h',
'FMRadioRequestChild.h',
'FMRadioRequestParent.h',
]
XPIDL_MODULE = 'dom_fm'
MODULE = 'dom'
LIBRARY_NAME = 'domfm_s'
CPP_SOURCES += [
'FMRadio.cpp',
'nsFMRadioSettings.cpp',
'FMRadioChild.cpp',
'FMRadioParent.cpp',
'FMRadioRequestChild.cpp',
'FMRadioRequestParent.cpp',
]
EXTRA_JS_MODULES += [
'DOMFMRadioParent.jsm',
]
EXTRA_COMPONENTS += [
'DOMFMRadio.manifest',
'DOMFMRadioChild.js',
]
FAIL_ON_WARNINGS = True
LIBXUL_LIBRARY = True
LIBRARY_NAME = 'domfmradio_s'

35
dom/fmradio/moz.build Normal file
View File

@ -0,0 +1,35 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
if CONFIG['MOZ_B2G_FM']:
DIRS += [
'ipc',
]
MODULE = 'dom'
EXPORTS.mozilla.dom += [
'FMRadio.h',
'FMRadioCommon.h',
'FMRadioService.h',
]
CPP_SOURCES += [
'FMRadio.cpp',
'FMRadioService.cpp',
]
LIBXUL_LIBRARY = True
LIBRARY_NAME = 'domfmradio_s'
IPDL_SOURCES += [
'ipc/PFMRadio.ipdl',
'ipc/PFMRadioRequest.ipdl',
]
FAIL_ON_WARNINGS = True

View File

@ -98,6 +98,7 @@
#include "mozilla/dom/mobilemessage/SmsChild.h"
#include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
#include "mozilla/dom/bluetooth/PBluetoothChild.h"
#include "mozilla/dom/PFMRadioChild.h"
#include "mozilla/ipc/InputStreamUtils.h"
#ifdef MOZ_WEBSPEECH
@ -954,6 +955,30 @@ ContentChild::DeallocPBluetoothChild(PBluetoothChild* aActor)
#endif
}
PFMRadioChild*
ContentChild::AllocPFMRadioChild()
{
#ifdef MOZ_B2G_FM
NS_RUNTIMEABORT("No one should be allocating PFMRadioChild actors");
return nullptr;
#else
NS_RUNTIMEABORT("No support for FMRadio on this platform!");
return nullptr;
#endif
}
bool
ContentChild::DeallocPFMRadioChild(PFMRadioChild* aActor)
{
#ifdef MOZ_B2G_FM
delete aActor;
return true;
#else
NS_RUNTIMEABORT("No support for FMRadio on this platform!");
return false;
#endif
}
PSpeechSynthesisChild*
ContentChild::AllocPSpeechSynthesisChild()
{

View File

@ -158,6 +158,9 @@ public:
virtual PBluetoothChild* AllocPBluetoothChild();
virtual bool DeallocPBluetoothChild(PBluetoothChild* aActor);
virtual PFMRadioChild* AllocPFMRadioChild();
virtual bool DeallocPFMRadioChild(PFMRadioChild* aActor);
virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild();
virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor);

View File

@ -32,6 +32,7 @@
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/DOMStorageIPC.h"
#include "mozilla/dom/bluetooth/PBluetoothParent.h"
#include "mozilla/dom/PFMRadioParent.h"
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
#include "SmsParent.h"
#include "mozilla/Hal.h"
@ -125,6 +126,11 @@ using namespace mozilla::system;
#endif
#include "JavaScriptParent.h"
#ifdef MOZ_B2G_FM
#include "mozilla/dom/FMRadioParent.h"
#endif
#include "Crypto.h"
#ifdef MOZ_WEBSPEECH
@ -2291,6 +2297,32 @@ ContentParent::RecvPBluetoothConstructor(PBluetoothParent* aActor)
#endif
}
PFMRadioParent*
ContentParent::AllocPFMRadioParent()
{
#ifdef MOZ_B2G_FM
if (!AssertAppProcessPermission(this, "fmradio")) {
return nullptr;
}
return new FMRadioParent();
#else
NS_WARNING("No support for FMRadio on this platform!");
return nullptr;
#endif
}
bool
ContentParent::DeallocPFMRadioParent(PFMRadioParent* aActor)
{
#ifdef MOZ_B2G_FM
delete aActor;
return true;
#else
NS_WARNING("No support for FMRadio on this platform!");
return false;
#endif
}
PSpeechSynthesisParent*
ContentParent::AllocPSpeechSynthesisParent()
{

View File

@ -328,6 +328,9 @@ private:
virtual bool DeallocPBluetoothParent(PBluetoothParent* aActor);
virtual bool RecvPBluetoothConstructor(PBluetoothParent* aActor);
virtual PFMRadioParent* AllocPFMRadioParent();
virtual bool DeallocPFMRadioParent(PFMRadioParent* aActor);
virtual PSpeechSynthesisParent* AllocPSpeechSynthesisParent();
virtual bool DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor);
virtual bool RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor);

View File

@ -32,6 +32,7 @@ LOCAL_INCLUDES += \
-I$(topsrcdir)/hal/sandbox \
-I$(topsrcdir)/dom/mobilemessage/src/ipc \
-I$(topsrcdir)/dom/devicestorage \
-I$(topsrcdir)/dom/fmradio/ipc \
-I$(topsrcdir)/widget/xpwidgets \
-I$(topsrcdir)/dom/bluetooth \
-I$(topsrcdir)/layout/base \

View File

@ -11,6 +11,7 @@ include protocol PCompositor;
include protocol PCrashReporter;
include protocol PExternalHelperApp;
include protocol PDeviceStorageRequest;
include protocol PFMRadio;
include protocol PHal;
include protocol PImageBridge;
include protocol PIndexedDB;
@ -126,6 +127,40 @@ union DeviceStorageParams
DeviceStorageAvailableParams;
};
struct FMRadioRequestEnableParams
{
double frequency;
};
struct FMRadioRequestDisableParams
{
};
struct FMRadioRequestSetFrequencyParams
{
double frequency;
};
struct FMRadioRequestSeekParams
{
bool upward;
};
struct FMRadioRequestCancelSeekParams
{
};
union FMRadioRequestParams
{
FMRadioRequestEnableParams;
FMRadioRequestDisableParams;
FMRadioRequestSetFrequencyParams;
FMRadioRequestSeekParams;
FMRadioRequestCancelSeekParams;
};
union PrefValue {
nsCString;
int32_t;
@ -154,6 +189,7 @@ rpc protocol PContent
manages PCrashReporter;
manages PDeviceStorageRequest;
manages PExternalHelperApp;
manages PFMRadio;
manages PHal;
manages PIndexedDB;
manages PMemoryReportRequest;
@ -319,6 +355,8 @@ parent:
PBluetooth();
PFMRadio();
// Services remoting
async StartVisitedQuery(URIParams uri);

View File

@ -568,15 +568,14 @@ TabChild::HandlePossibleViewportChange()
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
nsViewportInfo viewportInfo =
nsContentUtils::GetViewportInfo(document, mInnerSize.width, mInnerSize.height);
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
SendUpdateZoomConstraints(viewportInfo.IsZoomAllowed(),
CSSToScreenScale(viewportInfo.GetMinZoom()),
CSSToScreenScale(viewportInfo.GetMaxZoom()));
viewportInfo.GetMinZoom(),
viewportInfo.GetMaxZoom());
float screenW = mInnerSize.width;
float screenH = mInnerSize.height;
CSSSize viewport(viewportInfo.GetWidth(), viewportInfo.GetHeight());
CSSSize viewport(viewportInfo.GetSize());
// We're not being displayed in any way; don't bother doing anything because
// that will just confuse future adjustments.
@ -609,8 +608,6 @@ TabChild::HandlePossibleViewportChange()
return;
}
float minScale = 1.0f;
nsCOMPtr<Element> htmlDOMElement = document->GetHtmlElement();
HTMLBodyElement* bodyDOMElement = document->GetBodyElement();
@ -638,12 +635,11 @@ TabChild::HandlePossibleViewportChange()
return;
}
minScale = mInnerSize.width / pageSize.width;
minScale = clamped((double)minScale, viewportInfo.GetMinZoom(),
viewportInfo.GetMaxZoom());
NS_ENSURE_TRUE_VOID(minScale); // (return early rather than divide by 0)
CSSToScreenScale minScale(mInnerSize.width / pageSize.width);
minScale = clamped(minScale, viewportInfo.GetMinZoom(), viewportInfo.GetMaxZoom());
NS_ENSURE_TRUE_VOID(minScale.scale); // (return early rather than divide by 0)
viewport.height = std::max(viewport.height, screenH / minScale);
viewport.height = std::max(viewport.height, screenH / minScale.scale);
SetCSSViewport(viewport);
float oldScreenWidth = mLastMetrics.mCompositionBounds.width;
@ -680,14 +676,14 @@ TabChild::HandlePossibleViewportChange()
// FIXME/bug 799585(?): GetViewportInfo() returns a defaultZoom of
// 0.0 to mean "did not calculate a zoom". In that case, we default
// it to the intrinsic scale.
if (viewportInfo.GetDefaultZoom() < 0.01f) {
viewportInfo.SetDefaultZoom(metrics.CalculateIntrinsicScale().scale);
if (viewportInfo.GetDefaultZoom().scale < 0.01f) {
viewportInfo.SetDefaultZoom(metrics.CalculateIntrinsicScale());
}
double defaultZoom = viewportInfo.GetDefaultZoom();
CSSToScreenScale defaultZoom = viewportInfo.GetDefaultZoom();
MOZ_ASSERT(viewportInfo.GetMinZoom() <= defaultZoom &&
defaultZoom <= viewportInfo.GetMaxZoom());
metrics.mZoom = CSSToScreenScale(defaultZoom);
metrics.mZoom = defaultZoom;
}
metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort(

View File

@ -297,12 +297,16 @@ TabParent::ActorDestroy(ActorDestroyReason why)
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (frameLoader) {
nsCOMPtr<Element> frameElement(mFrameElement);
ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr);
frameLoader->DestroyChild();
if (why == AbnormalShutdown && os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
"oop-frameloader-crashed", nullptr);
nsContentUtils::DispatchTrustedEvent(frameElement->OwnerDoc(), frameElement,
NS_LITERAL_STRING("oop-browser-crashed"),
true, true);
}
}

View File

@ -33,3 +33,5 @@ phishingBlocked=The website at %S has been reported as a web forgery designed to
cspFrameAncestorBlocked=This page has a content security policy that prevents it from being embedded in this way.
corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected.
remoteXUL=This page uses an unsupported technology that is no longer available by default.
#LOCALIZATION NOTE (tabcrashed): The following string is shown in the tab title if a page with a blank title has crashed. Current UX says that the tab title should remain blank
tabcrashed=

View File

@ -49,6 +49,7 @@ PARALLEL_DIRS += [
'devicestorage',
'encoding',
'file',
'fmradio',
'media',
'messages',
'power',
@ -86,9 +87,6 @@ if CONFIG['MOZ_B2G_RIL']:
'voicemail',
]
if CONFIG['MOZ_B2G_FM']:
PARALLEL_DIRS += ['fm']
if CONFIG['MOZ_PAY']:
PARALLEL_DIRS += ['payment']

View File

@ -19,8 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=815105
var gData = [
{
perm: ["fmradio"],
needParentPerm: true,
obj: "mozFMRadio",
idl: "nsIDOMFMRadio",
webidl: "FMRadio",
},
]
</script>

View File

@ -67,6 +67,10 @@ interface BluetoothAdapter : EventTarget {
[SetterThrows]
attribute EventHandler onscostatuschanged;
// Fired when remote devices query current media play status
[SetterThrows]
attribute EventHandler onrequestmediaplaystatus;
[Creator, Throws]
DOMRequest setName(DOMString name);
[Creator, Throws]

102
dom/webidl/FMRadio.webidl Normal file
View File

@ -0,0 +1,102 @@
/* 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 FMRadio : EventTarget {
/* Indicates if the FM radio is enabled. */
readonly attribute boolean enabled;
/* Indicates if the antenna is plugged and available. */
readonly attribute boolean antennaAvailable;
/**
* Current frequency in MHz. The value will be null if the FM radio is
* disabled.
*/
readonly attribute double? frequency;
/* The upper bound of frequency in MHz. */
readonly attribute double frequencyUpperBound;
/* The lower bound of frequency in MHz. */
readonly attribute double frequencyLowerBound;
/**
* The difference in frequency between two "adjacent" channels, in MHz. That
* is, any two radio channels' frequencies differ by at least channelWidth
* MHz. Usually, the value is one of:
* - 0.05 MHz
* - 0.1 MHz
* - 0.2 MHz
*/
readonly attribute double channelWidth;
/* Fired when the FM radio is enabled. */
[SetterThrows]
attribute EventHandler onenabled;
/* Fired when the FM radio is disabled. */
[SetterThrows]
attribute EventHandler ondisabled;
/**
* Fired when the antenna becomes available or unavailable, i.e., fired when
* the antennaAvailable attribute changes.
*/
[SetterThrows]
attribute EventHandler onantennaavailablechange;
/* Fired when the FM radio's frequency is changed. */
[SetterThrows]
attribute EventHandler onfrequencychange;
/**
* Power the FM radio off. The disabled event will be fired if this request
* completes successfully.
*/
DOMRequest disable();
/**
* Power the FM radio on, and tune the radio to the given frequency in MHz.
* This will fail if the given frequency is out of range. The enabled event
* and frequencychange event will be fired if this request completes
* successfully.
*/
DOMRequest enable(double frequency);
/**
* Tune the FM radio to the given frequency. This will fail if the given
* frequency is out of range.
*
* Note that the FM radio may not tuned to the exact frequency given. To get
* the frequency the radio is actually tuned to, wait for the request to fire
* sucess (or wait for the frequencychange event to fire), and then read the
* frequency attribute.
*/
DOMRequest setFrequency(double frequency);
/**
* Tell the FM radio to seek up to the next channel. If the frequency is
* successfully changed, the frequencychange event will be triggered.
*
* Only one seek is allowed at once: If the radio is seeking when the seekUp
* is called, error will be fired.
*/
DOMRequest seekUp();
/**
* Tell the FM radio to seek down to the next channel. If the frequency is
* successfully changed, the frequencychange event will be triggered.
*
* Only one seek is allowed at once: If the radio is seeking when the
* seekDown is called, error will be fired.
*/
DOMRequest seekDown();
/**
* Cancel the seek action. If the radio is not currently seeking up or down,
* error will be fired.
*/
DOMRequest cancelSeek();
};

View File

@ -297,6 +297,13 @@ partial interface Navigator {
};
#endif // MOZ_B2G_BT
#ifdef MOZ_B2G_FM
partial interface Navigator {
[Throws, Func="Navigator::HasFMRadioSupport"]
readonly attribute FMRadio mozFMRadio;
};
#endif // MOZ_B2G_FM
#ifdef MOZ_TIME_MANAGER
// nsIDOMMozNavigatorTime
partial interface Navigator {

View File

@ -525,6 +525,10 @@ webidl_files += \
$(NULL)
endif
ifdef MOZ_B2G_FM
webidl_files += FMRadio.webidl
endif
ifdef ENABLE_TESTS
test_webidl_files := \
TestCodeGen.webidl \

View File

@ -115,6 +115,12 @@ struct SizeTyped :
};
typedef SizeTyped<UnknownUnits> Size;
template<class units>
IntSizeTyped<units> RoundedToInt(const SizeTyped<units>& aSize) {
return IntSizeTyped<units>(int32_t(floorf(aSize.width + 0.5f)),
int32_t(floorf(aSize.height + 0.5f)));
}
}
}

View File

@ -209,3 +209,7 @@ XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_ENDDOC , "Printing failed while c
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_STARTPAGE , "Printing failed while starting a new page.")
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY , "Cannot print this document yet, it is still being loaded.")
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_NO_XUL , "Printing XUL documents is not supported.") // bugs 136185 & 240490
/* Codes related to content */
XPC_MSG_DEF(NS_ERROR_CONTENT_CRASHED , "The process that hosted this content has crashed.")

View File

@ -231,6 +231,18 @@ gfx::RectTyped<dst> operator/(const gfx::IntRectTyped<src>& aRect, const gfx::Sc
float(aRect.height) / aScale.scale);
}
template<class src, class dst>
gfx::SizeTyped<dst> operator*(const gfx::SizeTyped<src>& aSize, const gfx::ScaleFactor<src, dst>& aScale) {
return gfx::SizeTyped<dst>(aSize.width * aScale.scale,
aSize.height * aScale.scale);
}
template<class src, class dst>
gfx::SizeTyped<dst> operator/(const gfx::SizeTyped<src>& aSize, const gfx::ScaleFactor<dst, src>& aScale) {
return gfx::SizeTyped<dst>(aSize.width / aScale.scale,
aSize.height / aScale.scale);
}
};
#endif

View File

@ -9612,9 +9612,9 @@ nsIPresShell::RecomputeFontSizeInflationEnabled()
screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
nsViewportInfo vInf =
nsContentUtils::GetViewportInfo(GetDocument(), screenWidth, screenHeight);
nsContentUtils::GetViewportInfo(GetDocument(), ScreenIntSize(screenWidth, screenHeight));
if (vInf.GetDefaultZoom() >= 1.0 || vInf.IsAutoSizeEnabled()) {
if (vInf.GetDefaultZoom() >= CSSToScreenScale(1.0f) || vInf.IsAutoSizeEnabled()) {
mFontSizeInflationEnabled = false;
return;
}

View File

@ -122,7 +122,8 @@ endif #}
ifdef MOZ_B2G_FM #{
SHARED_LIBRARY_LIBS += \
$(DEPTH)/dom/fm/$(LIB_PREFIX)domfm_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/fmradio/$(LIB_PREFIX)domfmradio_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/fmradio/ipc/$(LIB_PREFIX)domfmradio_s.$(LIB_SUFFIX) \
$(NULL)
endif #}
@ -326,7 +327,7 @@ LOCAL_INCLUDES += -I$(topsrcdir)/dom/system/gonk
endif #}
ifdef MOZ_B2G_FM #{
LOCAL_INCLUDES += -I$(topsrcdir)/dom/fm
LOCAL_INCLUDES += -I$(topsrcdir)/dom/fmradio
endif #}
ifdef MOZ_B2G_BT #{

View File

@ -125,11 +125,6 @@ using mozilla::dom::gonk::AudioManager;
using mozilla::system::nsVolumeService;
#endif
#ifdef MOZ_B2G_FM
#include "FMRadio.h"
using mozilla::dom::fm::FMRadio;
#endif
#include "AudioChannelAgent.h"
using mozilla::dom::AudioChannelAgent;
@ -303,10 +298,6 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsSynthVoiceRegistry,
NS_GENERIC_FACTORY_CONSTRUCTOR(AudioManager)
#endif
#ifdef MOZ_B2G_FM
NS_GENERIC_FACTORY_CONSTRUCTOR(FMRadio)
#endif
NS_GENERIC_FACTORY_CONSTRUCTOR(AudioChannelAgent)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceSensors)
@ -761,10 +752,6 @@ NS_DEFINE_NAMED_CID(NS_AUDIOMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_VOLUMESERVICE_CID);
#endif
#ifdef MOZ_B2G_FM
NS_DEFINE_NAMED_CID(NS_FMRADIO_CID);
#endif
NS_DEFINE_NAMED_CID(NS_AUDIOCHANNELAGENT_CID);
NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
@ -1049,9 +1036,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
#ifdef MOZ_WIDGET_GONK
{ &kNS_AUDIOMANAGER_CID, true, NULL, AudioManagerConstructor },
{ &kNS_VOLUMESERVICE_CID, true, NULL, nsVolumeServiceConstructor },
#endif
#ifdef MOZ_B2G_FM
{ &kNS_FMRADIO_CID, true, NULL, FMRadioConstructor },
#endif
{ &kNS_AUDIOCHANNELAGENT_CID, true, NULL, AudioChannelAgentConstructor },
{ &kNS_HTMLEDITOR_CID, false, NULL, nsHTMLEditorConstructor },
@ -1207,9 +1191,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
#ifdef MOZ_WIDGET_GONK
{ NS_AUDIOMANAGER_CONTRACTID, &kNS_AUDIOMANAGER_CID },
{ NS_VOLUMESERVICE_CONTRACTID, &kNS_VOLUMESERVICE_CID },
#endif
#ifdef MOZ_B2G_FM
{ NS_FMRADIO_CONTRACTID, &kNS_FMRADIO_CID },
#endif
{ NS_AUDIOCHANNELAGENT_CONTRACTID, &kNS_AUDIOCHANNELAGENT_CID },
{ "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },

View File

@ -255,7 +255,7 @@ this.OnRefTestLoad = function OnRefTestLoad(win)
#if BOOTSTRAP
#if REFTEST_B2G
var doc = gContainingWindow.document.getElementsByTagName("html")[0];
var doc = gContainingWindow.document.getElementsByTagName("window")[0];
#else
var doc = gContainingWindow.document.getElementById('main-window');
#endif

View File

@ -22,6 +22,7 @@ import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.FloatUtils;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.widget.GeckoActionProvider;
@ -74,6 +75,7 @@ import android.widget.Toast;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.EnumSet;
import java.util.List;
import java.util.Vector;
@ -1422,9 +1424,46 @@ abstract public class BrowserApp extends GeckoApp
animateHideHomePager();
hideBrowserSearch();
if (!TextUtils.isEmpty(url)) {
Tabs.getInstance().loadUrl(url, Tabs.LOADURL_USER_ENTERED);
// Don't do anything if the user entered an empty URL.
if (TextUtils.isEmpty(url)) {
return;
}
// If the URL doesn't look like a search query, just load it.
if (!StringUtils.isSearchQuery(url, true)) {
Tabs.getInstance().loadUrl(url, Tabs.LOADURL_USER_ENTERED);
return;
}
// Otherwise, check for a bookmark keyword.
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
final String keyword;
final String keywordSearch;
final int index = url.indexOf(" ");
if (index == -1) {
keyword = url;
keywordSearch = "";
} else {
keyword = url.substring(0, index);
keywordSearch = url.substring(index + 1);
}
final String keywordUrl = BrowserDB.getUrlForKeyword(getContentResolver(), keyword);
// If there isn't a bookmark keyword, just load the URL.
if (TextUtils.isEmpty(keywordUrl)) {
Tabs.getInstance().loadUrl(url, Tabs.LOADURL_USER_ENTERED);
return;
}
// Otherwise, construct a search query from the bookmark keyword.
final String searchUrl = keywordUrl.replace("%s", URLEncoder.encode(keywordSearch));
Tabs.getInstance().loadUrl(searchUrl, Tabs.LOADURL_USER_ENTERED);
}
});
}
boolean dismissEditingMode() {

View File

@ -141,6 +141,42 @@ abstract class AboutHomeTest extends BaseTest {
}
}
/**
* Updates the title and keyword of a bookmark with the given URL.
*
* Warning: This method assumes that there's only one bookmark with the given URL.
*/
protected void updateBookmark(String url, String title, String keyword) {
try {
ContentResolver resolver = getActivity().getContentResolver();
ClassLoader classLoader = getActivity().getClassLoader();
Class browserDB = classLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
Method getBookmarkForUrl = browserDB.getMethod("getBookmarkForUrl", ContentResolver.class, String.class);
// Get the id for the bookmark with the given URL.
Cursor c = null;
try {
c = (Cursor) getBookmarkForUrl.invoke(null, resolver, url);
if (!c.moveToFirst()) {
mAsserter.ok(false, "Getting bookmark with url", "Couldn't find bookmark with url = " + url);
return;
}
int id = c.getInt(c.getColumnIndexOrThrow("_id"));
Method updateBookmark = browserDB.getMethod("updateBookmark", ContentResolver.class, int.class, String.class, String.class, String.class);
updateBookmark.invoke(null, resolver, id, url, title, keyword);
mAsserter.ok(true, "Updating bookmark", "Updating bookmark with url = " + url);
} finally {
if (c != null) {
c.close();
}
}
} catch (Exception e) {
mAsserter.ok(false, "Exception updating bookmark: ", e.toString());
}
}
protected void deleteBookmark(String url) {
try {
ContentResolver resolver = getActivity().getContentResolver();

View File

@ -2,6 +2,7 @@
# [testAwesomebarSwipes] # disabled on fig - bug 880060
# [testBookmark] # disabled on fig - bug 880060
# [testBookmarklets] # disabled on fig - bug 880060
[testBookmarkKeyword]
[testBrowserSearchVisibility]
[testJNI]
# [testLoad] # see bug 851861

View File

@ -0,0 +1,36 @@
#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;
import @ANDROID_PACKAGE_NAME@.*;
public class testBookmarkKeyword extends AboutHomeTest {
@Override
protected int getTestType() {
return TEST_MOCHITEST;
}
public void testBookmarkKeyword() {
blockForGeckoReady();
final String url = getAbsoluteUrl("/robocop/robocop_blank_01.html");
final String title = "Browser Blank Page 01";
final String keyword = "testkeyword";
// Add a bookmark, and update it to have a keyword.
addOrUpdateMobileBookmark(title, url);
updateBookmark(url, title, keyword);
// Enter the keyword in the urlbar.
inputAndLoadUrl(keyword);
// Wait for the page to load.
waitForText(title);
// Make sure the title of the page appeared.
verifyPageTitle(title);
// Delete the bookmark to clean up.
deleteBookmark(url);
}
}

View File

@ -13,8 +13,7 @@ class testElementTouch(MarionetteTestCase):
button.tap()
expected = "button1-touchstart-touchend-mousemove-mousedown-mouseup-click"
self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
button = self.marionette.find_element("id", "button2")
button.tap()
button.tap(0, 300)
expected = "button2-touchstart-touchend-mousemove-mousedown-mouseup-click"
self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button2').innerHTML;") == expected)

View File

@ -9,7 +9,7 @@ const CHILD_SCRIPT = "chrome://specialpowers/content/specialpowers.js";
const CHILD_SCRIPT_API = "chrome://specialpowers/content/specialpowersAPI.js";
const CHILD_LOGGER_SCRIPT = "chrome://specialpowers/content/MozillaLogger.js";
let homescreen = document.getElementById('systemapp');
let homescreen = document.getElementById('homescreen');
let container = homescreen.contentWindow.document.getElementById('test-container');
function openWindow(aEvent) {

View File

@ -409,7 +409,7 @@ toolbar#nav-bar {
if options.browserChrome or options.chrome or options.a11y or options.webapprtChrome:
chrome += """
overlay chrome://browser/content/browser.xul chrome://mochikit/content/browser-test-overlay.xul
overlay chrome://browser/content/shell.xhtml chrome://mochikit/content/browser-test-overlay.xul
overlay chrome://browser/content/shell.xul chrome://mochikit/content/browser-test-overlay.xul
overlay chrome://navigator/content/navigator.xul chrome://mochikit/content/browser-test-overlay.xul
overlay chrome://webapprt/content/webapp.xul chrome://mochikit/content/browser-test-overlay.xul
"""

Some files were not shown because too many files have changed in this diff Show More