This commit is contained in:
Wes Kocher 2014-07-16 17:42:00 -07:00
Родитель 569fbb7417 8715ca584e
Коммит ab953e69d8
47 изменённых файлов: 2632 добавлений и 416 удалений

Просмотреть файл

@ -1013,7 +1013,7 @@ pref("services.sync.fxaccounts.enabled", true);
pref("identity.fxaccounts.enabled", true);
// Mobile Identity API.
pref("services.mobileid.server.uri", "https://msisdn-dev.stage.mozaws.net");
pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
// Enable mapped array buffer
pref("dom.mapped_arraybuffer.enabled", true);

Просмотреть файл

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

Просмотреть файл

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>

Просмотреть файл

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "8cd6c73ef83257a569d148e246108b2c161127bb",
"revision": "6739781fb8d0f3ae8bff65d1093e74d9f21ed6e5",
"repo_path": "/integration/gaia-central"
}

Просмотреть файл

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

Просмотреть файл

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>

Просмотреть файл

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d29773d2a011825fd77d1c0915a96eb0911417b6"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="5edca8cd06409b8bf404de4adf7ea08fc2940fd7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

Просмотреть файл

@ -9,6 +9,7 @@
#include "nsNetUtil.h"
#include "nsIScriptSecurityManager.h"
#include "mozilla/ArrayUtils.h"
#include "nsDOMString.h"
namespace mozilla {
namespace browser {
@ -19,6 +20,7 @@ struct RedirEntry {
const char* id;
const char* url;
uint32_t flags;
const char* idbOriginPostfix;
};
/*
@ -78,7 +80,8 @@ static RedirEntry kRedirMap[] = {
#endif
{ "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT },
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::ENABLE_INDEXED_DB },
{ "newtab", "chrome://browser/content/newtab/newTab.xul",
nsIAboutModule::ALLOW_SCRIPT },
{ "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
@ -100,11 +103,15 @@ static RedirEntry kRedirMap[] = {
{ "loopconversation", "chrome://browser/content/loop/conversation.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
nsIAboutModule::HIDE_FROM_ABOUTABOUT |
nsIAboutModule::ENABLE_INDEXED_DB },
{ "looppanel", "chrome://browser/content/loop/panel.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
nsIAboutModule::HIDE_FROM_ABOUTABOUT |
nsIAboutModule::ENABLE_INDEXED_DB,
// Shares an IndexedDB origin with about:loopconversation.
"loopconversation" },
};
static const int kRedirTotal = ArrayLength(kRedirMap);
@ -172,6 +179,29 @@ AboutRedirector::GetURIFlags(nsIURI *aURI, uint32_t *result)
return NS_ERROR_ILLEGAL_VALUE;
}
NS_IMETHODIMP
AboutRedirector::GetIndexedDBOriginPostfix(nsIURI *aURI, nsAString &result)
{
NS_ENSURE_ARG_POINTER(aURI);
nsAutoCString name = GetAboutModuleName(aURI);
for (int i = 0; i < kRedirTotal; i++) {
if (name.Equals(kRedirMap[i].id)) {
const char* postfix = kRedirMap[i].idbOriginPostfix;
if (!postfix) {
break;
}
result.AssignASCII(postfix);
return NS_OK;
}
}
SetDOMStringToNull(result);
return NS_ERROR_ILLEGAL_VALUE;
}
nsresult
AboutRedirector::Create(nsISupports *aOuter, REFNSIID aIID, void **result)
{

Просмотреть файл

@ -278,13 +278,19 @@ let gSyncPane = {
[data.email], 1);
let description = sb.GetStringFromName("firefoxAccountVerificationSentDescription");
Services.prompt.alert(window, title, heading + "\n\n" + description);
let factory = Cc["@mozilla.org/prompter;1"]
.getService(Ci.nsIPromptFactory);
let prompt = factory.getPrompt(window, Ci.nsIPrompt);
let bag = prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
bag.setPropertyAsBool("allowTabModal", true);
prompt.alert(title, heading + "\n\n" + description);
});
});
},
openOldSyncSupportPage: function() {
let url = Services.urlFormatter.formatURLPref('app.support.baseURL') + "old-sync"
let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "old-sync";
this.openContentInBrowser(url);
},
@ -304,13 +310,21 @@ let gSyncPane = {
let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
(ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
ps.BUTTON_POS_1_DEFAULT;
let pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
continueLabel, null, null, null, {});
let factory = Cc["@mozilla.org/prompter;1"]
.getService(Ci.nsIPromptFactory);
let prompt = factory.getPrompt(window, Ci.nsIPrompt);
let bag = prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
bag.setPropertyAsBool("allowTabModal", true);
let pressed = prompt.confirmEx(title, body, buttonFlags,
continueLabel, null, null, null, {});
if (pressed != 0) { // 0 is the "continue" button
return;
}
}
Components.utils.import('resource://gre/modules/FxAccounts.jsm');
Cu.import("resource://gre/modules/FxAccounts.jsm");
fxAccounts.signOut().then(() => {
this.updateWeavePrefs();
});

Просмотреть файл

@ -367,7 +367,7 @@ InspectorPanel.prototype = {
* Will store the current target url along with it to allow pre-selection at
* reload
*/
set selectionCssSelector(cssSelector) {
set selectionCssSelector(cssSelector = null) {
this._selectionCssSelector = {
selector: cssSelector,
url: this._target.url

Просмотреть файл

@ -43,7 +43,8 @@ support-files =
[browser_inspector_picker-stop-on-tool-change.js]
[browser_inspector_pseudoclass-lock.js]
[browser_inspector_pseudoclass-menu.js]
[browser_inspector_reload.js]
[browser_inspector_reload-01.js]
[browser_inspector_reload-02.js]
[browser_inspector_remove-iframe-during-load.js]
[browser_inspector_scrolling.js]
[browser_inspector_search-01.js]

Просмотреть файл

@ -0,0 +1,46 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
"use strict";
// A test to ensure reloading a page doesn't break the inspector.
// Reload should reselect the currently selected markup view element.
// This should work even when an element whose selector is inaccessible
// is selected (bug 1038651).
const TEST_URI = 'data:text/xml,<?xml version="1.0" standalone="no"?>' +
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"' +
' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' +
'<svg width="4cm" height="4cm" viewBox="0 0 400 400"' +
' xmlns="http://www.w3.org/2000/svg" version="1.1">' +
' <title>Example triangle01- simple example of a path</title>' +
' <desc>A path that draws a triangle</desc>' +
' <rect x="1" y="1" width="398" height="398"' +
' fill="none" stroke="blue" />' +
' <path d="M 100 100 L 300 100 L 200 300 z"' +
' fill="red" stroke="blue" stroke-width="3" />' +
'</svg>';
let test = asyncTest(function* () {
let { inspector, toolbox } = yield openInspectorForURL(TEST_URI);
let markupLoaded = inspector.once("markuploaded");
info("Reloading page.");
content.location.reload();
info("Waiting for markupview to load after reload.");
yield markupLoaded;
is(inspector.selection.node, getNode("svg"), "<svg> selected after reload.");
info("Selecting a node to see that inspector still works.");
yield selectNode("rect", inspector);
info("Reloading page.");
content.location.reload();
is(inspector.selection.node, getNode("rect"), "<rect> selected after reload.");
});

Просмотреть файл

@ -67,8 +67,10 @@
-moz-border-start: none !important;
-moz-border-end: 1px solid #bdbdbd;
min-width: 1px;
width: 1px;
background-image: none !important;
width: 3px;
-moz-margin-start: -3px;
position: relative;
background-image: none !important;
}
#placesToolbar > toolbarbutton {

Просмотреть файл

@ -1,51 +1,51 @@
menugroup > .menuitem-iconic[disabled="true"] > .menu-iconic-left {
opacity: .3;
}
#context-navigation > .menuitem-iconic {
-moz-box-flex: 1;
-moz-box-pack: center;
-moz-box-align: center;
}
#context-navigation > .menuitem-iconic > .menu-iconic-left {
-moz-appearance: none;
}
#context-navigation > #context-back > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/Toolbar.png");
-moz-image-region: rect(0, 36px, 18px, 18px);
}
#context-navigation > #context-forward > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/Toolbar.png");
-moz-image-region: rect(0, 72px, 18px, 54px);
}
#context-navigation > #context-reload > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-moz-image-region: rect(0, 14px, 14px, 0);
}
#context-navigation > #context-stop > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-moz-image-region: rect(0, 28px, 14px, 14px);
}
#context-navigation > #context-bookmarkpage > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/Toolbar.png");
-moz-image-region: rect(0, 144px, 18px, 126px);
}
#context-back:-moz-locale-dir(rtl),
#context-forward:-moz-locale-dir(rtl),
#context-reload:-moz-locale-dir(rtl) {
transform: scaleX(-1);
}
#context-navigation > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon {
width: 18px;
height: 18px;
margin: 7px;
}
menugroup > .menuitem-iconic[disabled="true"] > .menu-iconic-left {
opacity: .3;
}
#context-navigation > .menuitem-iconic {
-moz-box-flex: 1;
-moz-box-pack: center;
-moz-box-align: center;
}
#context-navigation > .menuitem-iconic > .menu-iconic-left {
-moz-appearance: none;
}
#context-navigation > #context-back > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/Toolbar.png");
-moz-image-region: rect(0, 36px, 18px, 18px);
}
#context-navigation > #context-forward > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/Toolbar.png");
-moz-image-region: rect(0, 72px, 18px, 54px);
}
#context-navigation > #context-reload > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-moz-image-region: rect(0, 14px, 14px, 0);
}
#context-navigation > #context-stop > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-moz-image-region: rect(0, 28px, 14px, 14px);
}
#context-navigation > #context-bookmarkpage > .menu-iconic-left {
list-style-image: url("chrome://browser/skin/Toolbar.png");
-moz-image-region: rect(0, 144px, 18px, 126px);
}
#context-back:-moz-locale-dir(rtl),
#context-forward:-moz-locale-dir(rtl),
#context-reload:-moz-locale-dir(rtl) {
transform: scaleX(-1);
}
#context-navigation > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon {
width: 18px;
height: 18px;
margin: 7px;
}

Просмотреть файл

@ -8,6 +8,7 @@
#include "nsNetUtil.h"
#include "nsAboutProtocolUtils.h"
#include "mozilla/ArrayUtils.h"
#include "nsDOMString.h"
NS_IMPL_ISUPPORTS(nsAboutRedirector, nsIAboutModule)
@ -136,6 +137,13 @@ nsAboutRedirector::GetURIFlags(nsIURI *aURI, uint32_t *result)
return NS_ERROR_ILLEGAL_VALUE;
}
NS_IMETHODIMP
nsAboutRedirector::GetIndexedDBOriginPostfix(nsIURI *aURI, nsAString &result)
{
SetDOMStringToNull(result);
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsAboutRedirector::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{

Просмотреть файл

@ -68,6 +68,7 @@
#include "mozilla/MouseEvents.h"
#include "AudioChannelService.h"
#include "MessageEvent.h"
#include "nsAboutProtocolUtils.h"
// Interfaces Needed
#include "nsIFrame.h"
@ -10513,6 +10514,20 @@ nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
return rv.ErrorCode();
}
static bool
GetIndexedDBEnabledForAboutURI(nsIURI *aURI)
{
nsCOMPtr<nsIAboutModule> module;
nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(module));
NS_ENSURE_SUCCESS(rv, false);
uint32_t flags;
rv = module->GetURIFlags(aURI, &flags);
NS_ENSURE_SUCCESS(rv, false);
return flags & nsIAboutModule::ENABLE_INDEXED_DB;
}
indexedDB::IDBFactory*
nsGlobalWindow::GetIndexedDB(ErrorResult& aError)
{
@ -10533,11 +10548,12 @@ nsGlobalWindow::GetIndexedDB(ErrorResult& aError)
if (principal) {
nsCOMPtr<nsIURI> uri;
principal->GetURI(getter_AddRefs(uri));
bool isAbout = false;
if (uri && NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
nsAutoCString path;
skipThirdPartyCheck = NS_SUCCEEDED(uri->GetPath(path)) &&
path.EqualsLiteral("home");
if (uri) {
bool isAbout = false;
if (NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
skipThirdPartyCheck = GetIndexedDBEnabledForAboutURI(uri);
}
}
}

Просмотреть файл

@ -4,9 +4,14 @@
* 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 <errno.h>
#include <sys/socket.h>
#include <unistd.h>
#include "base/message_loop.h"
#include "BluetoothInterface.h"
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
BEGIN_BLUETOOTH_NAMESPACE
@ -69,6 +74,40 @@ private:
Arg1 mArg1;
};
template <typename Obj, typename Res,
typename Arg1, typename Arg2, typename Arg3>
class BluetoothInterfaceRunnable3 : public nsRunnable
{
public:
BluetoothInterfaceRunnable3(Obj* aObj,
Res (Obj::*aMethod)(Arg1, Arg2, Arg3),
const Arg1& aArg1, const Arg2& aArg2,
const Arg3& aArg3)
: mObj(aObj)
, mMethod(aMethod)
, mArg1(aArg1)
, mArg2(aArg2)
, mArg3(aArg3)
{
MOZ_ASSERT(mObj);
MOZ_ASSERT(mMethod);
}
NS_METHOD
Run() MOZ_OVERRIDE
{
((*mObj).*mMethod)(mArg1, mArg2, mArg3);
return NS_OK;
}
private:
nsRefPtr<Obj> mObj;
void (Obj::*mMethod)(Arg1, Arg2, Arg3);
Arg1 mArg1;
Arg2 mArg2;
Arg3 mArg3;
};
//
// Socket Interface
//
@ -84,23 +123,432 @@ struct interface_traits<BluetoothSocketInterface>
}
};
bt_status_t
BluetoothSocketInterface::Listen(btsock_type_t aType,
const char* aServiceName,
const uint8_t* aServiceUuid, int aChannel,
int& aSockFd, int aFlags)
typedef
BluetoothInterfaceRunnable1<BluetoothSocketResultHandler, void, int>
BluetoothSocketIntResultRunnable;
typedef
BluetoothInterfaceRunnable3<BluetoothSocketResultHandler,
void, int, const nsAString_internal&, int>
BluetoothSocketIntStringIntResultRunnable;
typedef
BluetoothInterfaceRunnable1<BluetoothSocketResultHandler, void, bt_status_t>
BluetoothSocketErrorRunnable;
static nsresult
DispatchBluetoothSocketResult(BluetoothSocketResultHandler* aRes,
void (BluetoothSocketResultHandler::*aMethod)(int),
int aArg, bt_status_t aStatus)
{
return mInterface->listen(aType, aServiceName, aServiceUuid, aChannel,
&aSockFd, aFlags);
MOZ_ASSERT(aRes);
nsRunnable* runnable;
if (aStatus == BT_STATUS_SUCCESS) {
runnable = new BluetoothSocketIntResultRunnable(aRes, aMethod, aArg);
} else {
runnable = new BluetoothSocketErrorRunnable(aRes,
&BluetoothSocketResultHandler::OnError, aStatus);
}
nsresult rv = NS_DispatchToMainThread(runnable);
if (NS_FAILED(rv)) {
BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
}
return rv;
}
bt_status_t
static nsresult
DispatchBluetoothSocketResult(
BluetoothSocketResultHandler* aRes,
void (BluetoothSocketResultHandler::*aMethod)(int, const nsAString&, int),
int aArg1, const nsAString& aArg2, int aArg3, bt_status_t aStatus)
{
MOZ_ASSERT(aRes);
nsRunnable* runnable;
if (aStatus == BT_STATUS_SUCCESS) {
runnable = new BluetoothSocketIntStringIntResultRunnable(aRes, aMethod,
aArg1, aArg2,
aArg3);
} else {
runnable = new BluetoothSocketErrorRunnable(aRes,
&BluetoothSocketResultHandler::OnError, aStatus);
}
nsresult rv = NS_DispatchToMainThread(runnable);
if (NS_FAILED(rv)) {
BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
}
return rv;
}
void
BluetoothSocketInterface::Listen(btsock_type_t aType,
const char* aServiceName,
const uint8_t* aServiceUuid,
int aChannel, int aFlags,
BluetoothSocketResultHandler* aRes)
{
int fd;
bt_status_t status = mInterface->listen(aType, aServiceName, aServiceUuid,
aChannel, &fd, aFlags);
if (aRes) {
DispatchBluetoothSocketResult(aRes, &BluetoothSocketResultHandler::Listen,
fd, status);
}
}
#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
/* |SocketMessageWatcher| receives Bluedroid's socket setup
* messages on the I/O thread. You need to inherit from this
* class to make use of it.
*
* Bluedroid sends two socket info messages (20 bytes) at
* the beginning of a connection to both peers.
*
* - 1st message: [channel:4]
* - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
*
* On the server side, the second message will contain a
* socket file descriptor for the connection. The client
* uses the original file descriptor.
*/
class SocketMessageWatcher : public MessageLoopForIO::Watcher
{
public:
static const unsigned char MSG1_SIZE = 4;
static const unsigned char MSG2_SIZE = 16;
static const unsigned char OFF_CHANNEL1 = 0;
static const unsigned char OFF_SIZE = 4;
static const unsigned char OFF_BDADDRESS = 6;
static const unsigned char OFF_CHANNEL2 = 12;
static const unsigned char OFF_STATUS = 16;
SocketMessageWatcher(int aFd)
: mFd(aFd)
, mClientFd(-1)
, mLen(0)
{ }
virtual ~SocketMessageWatcher()
{ }
virtual void Proceed(bt_status_t aStatus) = 0;
void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE
{
bt_status_t status;
switch (mLen) {
case 0:
status = RecvMsg1();
break;
case MSG1_SIZE:
status = RecvMsg2();
break;
default:
/* message-size error */
status = BT_STATUS_FAIL;
break;
}
if (IsComplete() || status != BT_STATUS_SUCCESS) {
mWatcher.StopWatchingFileDescriptor();
Proceed(status);
}
}
void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE
{ }
void Watch()
{
MessageLoopForIO::current()->WatchFileDescriptor(
mFd,
true,
MessageLoopForIO::WATCH_READ,
&mWatcher,
this);
}
bool IsComplete() const
{
return mLen == (MSG1_SIZE + MSG2_SIZE);
}
int GetFd() const
{
return mFd;
}
int32_t GetChannel1() const
{
return ReadInt32(OFF_CHANNEL1);
}
int32_t GetSize() const
{
return ReadInt16(OFF_SIZE);
}
nsString GetBdAddress() const
{
nsString bdAddress;
ReadBdAddress(OFF_BDADDRESS, bdAddress);
return bdAddress;
}
int32_t GetChannel2() const
{
return ReadInt32(OFF_CHANNEL2);
}
int32_t GetConnectionStatus() const
{
return ReadInt32(OFF_STATUS);
}
int GetClientFd() const
{
return mClientFd;
}
private:
bt_status_t RecvMsg1()
{
struct iovec iv;
memset(&iv, 0, sizeof(iv));
iv.iov_base = mBuf;
iv.iov_len = MSG1_SIZE;
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
if (res < 0) {
return BT_STATUS_FAIL;
}
mLen += res;
return BT_STATUS_SUCCESS;
}
bt_status_t RecvMsg2()
{
struct iovec iv;
memset(&iv, 0, sizeof(iv));
iv.iov_base = mBuf + MSG1_SIZE;
iv.iov_len = MSG2_SIZE;
struct msghdr msg;
struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
if (res < 0) {
return BT_STATUS_FAIL;
}
mLen += res;
if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
return BT_STATUS_FAIL;
}
struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
// Extract client fd from message header
for (; cmsgptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
if (CMSGHDR_CONTAINS_FD(cmsgptr)) {
// if multiple file descriptors have been sent, we close
// all but the final one.
if (mClientFd != -1) {
TEMP_FAILURE_RETRY(close(mClientFd));
}
// retrieve sent client fd
mClientFd = *(static_cast<int*>(CMSG_DATA(cmsgptr)));
}
}
return BT_STATUS_SUCCESS;
}
int16_t ReadInt16(unsigned long aOffset) const
{
/* little-endian buffer */
return (static_cast<int16_t>(mBuf[aOffset + 1]) << 8) |
static_cast<int16_t>(mBuf[aOffset]);
}
int32_t ReadInt32(unsigned long aOffset) const
{
/* little-endian buffer */
return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
(static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
(static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
static_cast<int32_t>(mBuf[aOffset]);
}
void ReadBdAddress(unsigned long aOffset, nsAString& aBdAddress) const
{
char str[18];
sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
mBuf[aOffset + 0], mBuf[aOffset + 1], mBuf[aOffset + 2],
mBuf[aOffset + 3], mBuf[aOffset + 4], mBuf[aOffset + 5]);
aBdAddress.AssignLiteral(str);
}
MessageLoopForIO::FileDescriptorWatcher mWatcher;
int mFd;
int mClientFd;
unsigned char mLen;
uint8_t mBuf[MSG1_SIZE + MSG2_SIZE];
};
/* |SocketMessageWatcherTask| starts a SocketMessageWatcher
* on the I/O task
*/
class SocketMessageWatcherTask MOZ_FINAL : public Task
{
public:
SocketMessageWatcherTask(SocketMessageWatcher* aWatcher)
: mWatcher(aWatcher)
{
MOZ_ASSERT(mWatcher);
}
void Run() MOZ_OVERRIDE
{
mWatcher->Watch();
}
private:
SocketMessageWatcher* mWatcher;
};
/* |DeleteTask| deletes a class instance on the I/O thread
*/
template <typename T>
class DeleteTask MOZ_FINAL : public Task
{
public:
DeleteTask(T* aPtr)
: mPtr(aPtr)
{ }
void Run() MOZ_OVERRIDE
{
mPtr = nullptr;
}
private:
nsAutoPtr<T> mPtr;
};
/* |ConnectWatcher| specializes SocketMessageWatcher for
* connect operations by reading the socket messages from
* Bluedroid and forwarding the connected socket to the
* resource handler.
*/
class ConnectWatcher MOZ_FINAL : public SocketMessageWatcher
{
public:
ConnectWatcher(int aFd, BluetoothSocketResultHandler* aRes)
: SocketMessageWatcher(aFd)
, mRes(aRes)
{ }
void Proceed(bt_status_t aStatus) MOZ_OVERRIDE
{
if (mRes) {
DispatchBluetoothSocketResult(mRes,
&BluetoothSocketResultHandler::Connect,
GetFd(), GetBdAddress(),
GetConnectionStatus(), aStatus);
}
MessageLoopForIO::current()->PostTask(
FROM_HERE, new DeleteTask<ConnectWatcher>(this));
}
private:
nsRefPtr<BluetoothSocketResultHandler> mRes;
};
void
BluetoothSocketInterface::Connect(const bt_bdaddr_t* aBdAddr,
btsock_type_t aType, const uint8_t* aUuid,
int aChannel, int& aSockFd, int aFlags)
int aChannel, int aFlags,
BluetoothSocketResultHandler* aRes)
{
return mInterface->connect(aBdAddr, aType, aUuid, aChannel, &aSockFd,
aFlags);
int fd;
bt_status_t status = mInterface->connect(aBdAddr, aType, aUuid, aChannel,
&fd, aFlags);
if (status == BT_STATUS_SUCCESS) {
/* receive Bluedroid's socket-setup messages */
Task* t = new SocketMessageWatcherTask(new ConnectWatcher(fd, aRes));
XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
} else if (aRes) {
DispatchBluetoothSocketResult(aRes,
&BluetoothSocketResultHandler::Connect,
-1, EmptyString(), 0, status);
}
}
/* |AcceptWatcher| specializes |SocketMessageWatcher| for accept
* operations by reading the socket messages from Bluedroid and
* forwarding the received client socket to the resource handler.
* The first message is received immediately. When there's a new
* connection, Bluedroid sends the 2nd message with the socket
* info and socket file descriptor.
*/
class AcceptWatcher MOZ_FINAL : public SocketMessageWatcher
{
public:
AcceptWatcher(int aFd, BluetoothSocketResultHandler* aRes)
: SocketMessageWatcher(aFd)
, mRes(aRes)
{
/* not supplying a result handler leaks received file descriptor */
MOZ_ASSERT(mRes);
}
void Proceed(bt_status_t aStatus) MOZ_OVERRIDE
{
if (mRes) {
DispatchBluetoothSocketResult(mRes,
&BluetoothSocketResultHandler::Accept,
GetClientFd(), GetBdAddress(),
GetConnectionStatus(),
aStatus);
}
MessageLoopForIO::current()->PostTask(
FROM_HERE, new DeleteTask<AcceptWatcher>(this));
}
private:
nsRefPtr<BluetoothSocketResultHandler> mRes;
};
void
BluetoothSocketInterface::Accept(int aFd, BluetoothSocketResultHandler* aRes)
{
/* receive Bluedroid's socket-setup messages and client fd */
Task* t = new SocketMessageWatcherTask(new AcceptWatcher(aFd, aRes));
XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
}
BluetoothSocketInterface::BluetoothSocketInterface(

Просмотреть файл

@ -24,6 +24,25 @@ class BluetoothInterface;
// Socket Interface
//
class BluetoothSocketResultHandler
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothSocketResultHandler)
virtual ~BluetoothSocketResultHandler() { }
virtual void OnError(bt_status_t aStatus)
{
BT_WARNING("Received error code %d", (int)aStatus);
}
virtual void Listen(int aSockFd) { }
virtual void Connect(int aSockFd, const nsAString& aBdAddress,
int aConnectionState) { }
virtual void Accept(int aSockFd, const nsAString& aBdAddress,
int aConnectionState) { }
};
class BluetoothSocketInterface
{
public:
@ -31,13 +50,15 @@ public:
// Init and Cleanup is handled by BluetoothInterface
bt_status_t Listen(btsock_type_t aType,
const char* aServiceName, const uint8_t* aServiceUuid,
int aChannel, int& aSockFd, int aFlags);
void Listen(btsock_type_t aType,
const char* aServiceName, const uint8_t* aServiceUuid,
int aChannel, int aFlags, BluetoothSocketResultHandler* aRes);
bt_status_t Connect(const bt_bdaddr_t* aBdAddr, btsock_type_t aType,
const uint8_t* aUuid, int aChannel, int& aSockFd,
int aFlags);
void Connect(const bt_bdaddr_t* aBdAddr, btsock_type_t aType,
const uint8_t* aUuid, int aChannel, int aFlags,
BluetoothSocketResultHandler* aRes);
void Accept(int aFd, BluetoothSocketResultHandler* aRes);
protected:
BluetoothSocketInterface(const btsock_interface_t* aInterface);

Просмотреть файл

@ -18,9 +18,6 @@
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#define FIRST_SOCKET_INFO_MSG_LENGTH 4
#define TOTAL_SOCKET_INFO_LENGTH 20
using namespace mozilla::ipc;
USING_BLUETOOTH_NAMESPACE
@ -48,61 +45,55 @@ EnsureBluetoothSocketHalLoad()
return true;
}
static int16_t
ReadInt16(const uint8_t* aData, size_t* aOffset)
{
int16_t value = (aData[*aOffset + 1] << 8) | aData[*aOffset];
*aOffset += 2;
return value;
}
static int32_t
ReadInt32(const uint8_t* aData, size_t* aOffset)
{
int32_t value = (aData[*aOffset + 3] << 24) |
(aData[*aOffset + 2] << 16) |
(aData[*aOffset + 1] << 8) |
aData[*aOffset];
*aOffset += 4;
return value;
}
static void
ReadBdAddress(const uint8_t* aData, size_t* aOffset, nsAString& aDeviceAddress)
{
char bdstr[18];
sprintf(bdstr, "%02x:%02x:%02x:%02x:%02x:%02x",
aData[*aOffset], aData[*aOffset + 1], aData[*aOffset + 2],
aData[*aOffset + 3], aData[*aOffset + 4], aData[*aOffset + 5]);
aDeviceAddress.AssignLiteral(bdstr);
*aOffset += 6;
}
class mozilla::dom::bluetooth::DroidSocketImpl : public ipc::UnixFdWatcher
{
public:
/* The connection status in DroidSocketImpl indicates the current
* phase of the socket connection. The initial settign should always
* be DISCONNECTED, when no connection is present.
*
* To establish a connection on the server, DroidSocketImpl moves
* to LISTENING. It now waits for incoming connection attempts by
* installing a read watcher on the I/O thread. When its socket file
* descriptor becomes readable, DroidSocketImpl accepts the connection
* and finally moves DroidSocketImpl to CONNECTED. DroidSocketImpl now
* uses read and write watchers during data transfers. Any socket setup
* is handled internally by the accept method.
*
* On the client side, DroidSocketImpl moves to CONNECTING and installs
* a write watcher on the I/O thread to wait until the connection is
* ready. The socket setup is handled internally by the connect method.
* Installing the write handler makes the code compatible with POSIX
* semantics for non-blocking connects and gives a clear signal when the
* conncetion is ready. DroidSocketImpl then moves to CONNECTED and uses
* read and write watchers during data transfers.
*/
enum ConnectionStatus {
SOCKET_IS_DISCONNECTED = 0,
SOCKET_IS_LISTENING,
SOCKET_IS_CONNECTING,
SOCKET_IS_CONNECTED
};
DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer, int aFd)
: ipc::UnixFdWatcher(aIOLoop, aFd)
, mConsumer(aConsumer)
, mReadMsgForClientFd(false)
, mShuttingDownOnIOThread(false)
, mChannel(0)
, mAuth(false)
, mEncrypt(false)
{
}
, mConnectionStatus(SOCKET_IS_DISCONNECTED)
{ }
DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer,
int aChannel, bool aAuth, bool aEncrypt)
: ipc::UnixFdWatcher(aIOLoop)
, mConsumer(aConsumer)
, mReadMsgForClientFd(false)
, mShuttingDownOnIOThread(false)
, mChannel(aChannel)
, mAuth(aAuth)
, mEncrypt(aEncrypt)
, mConnectionStatus(SOCKET_IS_DISCONNECTED)
{ }
DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer,
@ -110,12 +101,12 @@ public:
int aChannel, bool aAuth, bool aEncrypt)
: ipc::UnixFdWatcher(aIOLoop)
, mConsumer(aConsumer)
, mReadMsgForClientFd(false)
, mShuttingDownOnIOThread(false)
, mDeviceAddress(aDeviceAddress)
, mChannel(aChannel)
, mAuth(aAuth)
, mEncrypt(aEncrypt)
, mConnectionStatus(SOCKET_IS_DISCONNECTED)
{
MOZ_ASSERT(!mDeviceAddress.IsEmpty());
}
@ -159,24 +150,20 @@ public:
mShuttingDownOnIOThread = true;
}
void Connect();
void Listen();
void SetUpIO(bool aWrite)
{
AddWatchers(READ_WATCHER, true);
if (aWrite) {
AddWatchers(WRITE_WATCHER, false);
}
}
void Connect(int aFd);
void Listen(int aFd);
void Accept(int aFd);
void ConnectClientFd()
{
// Stop current read watch
RemoveWatchers(READ_WATCHER);
mConnectionStatus = SOCKET_IS_CONNECTED;
// Restart read & write watch on client fd
SetUpIO(true);
AddWatchers(READ_WATCHER, true);
AddWatchers(WRITE_WATCHER, false);
}
/**
@ -186,11 +173,6 @@ public:
*/
RefPtr<BluetoothSocket> mConsumer;
/**
* If true, read message header to get client fd.
*/
bool mReadMsgForClientFd;
private:
/**
* libevent triggered functions that reads data from socket when available and
@ -208,14 +190,10 @@ private:
*/
virtual void OnFileCanWriteWithoutBlocking(int aFd);
/**
* Read message to get data and client fd wrapped in message header
*
* @param aFd [in] File descriptor to read message from
* @param aBuffer [out] Data buffer read
* @param aLength [out] Number of bytes read
*/
ssize_t ReadMsg(int aFd, void *aBuffer, size_t aLength);
void OnSocketCanReceiveWithoutBlocking(int aFd);
void OnSocketCanAcceptWithoutBlocking(int aFd);
void OnSocketCanSendWithoutBlocking(int aFd);
void OnSocketCanConnectWithoutBlocking(int aFd);
/**
* Raw data queue. Must be pushed/popped from IO thread only.
@ -232,6 +210,7 @@ private:
int mChannel;
bool mAuth;
bool mEncrypt;
ConnectionStatus mConnectionStatus;
};
template<class T>
@ -253,6 +232,70 @@ private:
T* mInstance;
};
class DroidSocketImplRunnable : public nsRunnable
{
public:
DroidSocketImpl* GetImpl() const
{
return mImpl;
}
protected:
DroidSocketImplRunnable(DroidSocketImpl* aImpl)
: mImpl(aImpl)
{
MOZ_ASSERT(aImpl);
}
virtual ~DroidSocketImplRunnable()
{ }
private:
DroidSocketImpl* mImpl;
};
class OnSocketEventRunnable : public DroidSocketImplRunnable
{
public:
enum SocketEvent {
CONNECT_SUCCESS,
CONNECT_ERROR,
DISCONNECT
};
OnSocketEventRunnable(DroidSocketImpl* aImpl, SocketEvent e)
: DroidSocketImplRunnable(aImpl)
, mEvent(e)
{
MOZ_ASSERT(!NS_IsMainThread());
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
DroidSocketImpl* impl = GetImpl();
if (impl->IsShutdownOnMainThread()) {
NS_WARNING("CloseSocket has already been called!");
// Since we've already explicitly closed and the close happened before
// this, this isn't really an error. Since we've warned, return OK.
return NS_OK;
}
if (mEvent == CONNECT_SUCCESS) {
impl->mConsumer->NotifySuccess();
} else if (mEvent == CONNECT_ERROR) {
impl->mConsumer->NotifyError();
} else if (mEvent == DISCONNECT) {
impl->mConsumer->NotifyDisconnect();
}
return NS_OK;
}
private:
SocketEvent mEvent;
};
class RequestClosingSocketTask : public nsRunnable
{
public:
@ -392,32 +435,40 @@ private:
class SocketConnectTask : public DroidSocketImplTask
{
public:
SocketConnectTask(DroidSocketImpl* aDroidSocketImpl)
SocketConnectTask(DroidSocketImpl* aDroidSocketImpl, int aFd)
: DroidSocketImplTask(aDroidSocketImpl)
, mFd(aFd)
{ }
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!IsCanceled());
GetDroidSocketImpl()->Connect();
GetDroidSocketImpl()->Connect(mFd);
}
private:
int mFd;
};
class SocketListenTask : public DroidSocketImplTask
{
public:
SocketListenTask(DroidSocketImpl* aDroidSocketImpl)
SocketListenTask(DroidSocketImpl* aDroidSocketImpl, int aFd)
: DroidSocketImplTask(aDroidSocketImpl)
, mFd(aFd)
{ }
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(!NS_IsMainThread());
if (!IsCanceled()) {
GetDroidSocketImpl()->Listen();
GetDroidSocketImpl()->Listen(mFd);
}
}
private:
int mFd;
};
class SocketConnectClientFdTask : public Task
@ -434,127 +485,83 @@ public:
};
void
DroidSocketImpl::Connect()
DroidSocketImpl::Connect(int aFd)
{
MOZ_ASSERT(sBluetoothSocketInterface);
MOZ_ASSERT(aFd >= 0);
bt_bdaddr_t remoteBdAddress;
StringToBdAddressType(mDeviceAddress, &remoteBdAddress);
// TODO: uuid as argument
int fd = -1;
bt_status_t status =
sBluetoothSocketInterface->Connect(&remoteBdAddress,
BTSOCK_RFCOMM,
UUID_OBEX_OBJECT_PUSH,
mChannel,
fd,
(BTSOCK_FLAG_ENCRYPT * mEncrypt) |
(BTSOCK_FLAG_AUTH * mAuth));
NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
NS_ENSURE_TRUE_VOID(fd >= 0);
int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
NS_ENSURE_TRUE_VOID(flags >= 0);
if (!(flags & O_NONBLOCK)) {
int res = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, flags | O_NONBLOCK));
int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
NS_ENSURE_TRUE_VOID(!res);
}
SetFd(fd);
SetFd(aFd);
mConnectionStatus = SOCKET_IS_CONNECTING;
AddWatchers(READ_WATCHER, true);
AddWatchers(WRITE_WATCHER, false);
}
void
DroidSocketImpl::Listen()
DroidSocketImpl::Listen(int aFd)
{
MOZ_ASSERT(sBluetoothSocketInterface);
MOZ_ASSERT(aFd >= 0);
// TODO: uuid and service name as arguments
int fd = -1;
bt_status_t status =
sBluetoothSocketInterface->Listen(BTSOCK_RFCOMM,
"OBEX Object Push",
UUID_OBEX_OBJECT_PUSH,
mChannel,
fd,
(BTSOCK_FLAG_ENCRYPT * mEncrypt) |
(BTSOCK_FLAG_AUTH * mAuth));
NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
NS_ENSURE_TRUE_VOID(fd >= 0);
int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
NS_ENSURE_TRUE_VOID(flags >= 0);
if (!(flags & O_NONBLOCK)) {
int res = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, flags | O_NONBLOCK));
int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
NS_ENSURE_TRUE_VOID(!res);
}
SetFd(fd);
SetFd(aFd);
mConnectionStatus = SOCKET_IS_LISTENING;
AddWatchers(READ_WATCHER, true);
}
ssize_t
DroidSocketImpl::ReadMsg(int aFd, void *aBuffer, size_t aLength)
void
DroidSocketImpl::Accept(int aFd)
{
ssize_t ret;
struct msghdr msg;
struct iovec iv;
struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
Close();
memset(&msg, 0, sizeof(msg));
memset(&iv, 0, sizeof(iv));
int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
NS_ENSURE_TRUE_VOID(flags >= 0);
iv.iov_base = (unsigned char *)aBuffer;
iv.iov_len = aLength;
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
ret = recvmsg(GetFd(), &msg, MSG_NOSIGNAL);
if (ret < 0 && errno == EPIPE) {
// Treat this as an end of stream
return 0;
if (!(flags & O_NONBLOCK)) {
int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
NS_ENSURE_TRUE_VOID(!res);
}
NS_ENSURE_FALSE(ret < 0, -1);
NS_ENSURE_FALSE(msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE), -1);
SetFd(aFd);
mConnectionStatus = SOCKET_IS_CONNECTED;
// Extract client fd from message header
for (struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
cmsgptr != nullptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
if (cmsgptr->cmsg_level != SOL_SOCKET) {
continue;
}
if (cmsgptr->cmsg_type == SCM_RIGHTS) {
int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
nsRefPtr<OnSocketEventRunnable> r =
new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
NS_DispatchToMainThread(r);
// Overwrite fd with client fd
int fd = pDescriptors[0];
int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
NS_ENSURE_TRUE(flags >= 0, 0);
if (!(flags & O_NONBLOCK)) {
int res = TEMP_FAILURE_RETRY(fcntl(fd, F_SETFL, flags | O_NONBLOCK));
NS_ENSURE_TRUE(!res, 0);
}
Close();
SetFd(fd);
break;
}
AddWatchers(READ_WATCHER, true);
if (!mOutgoingQ.IsEmpty()) {
AddWatchers(WRITE_WATCHER, false);
}
return ret;
}
void
DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
{
if (mConnectionStatus == SOCKET_IS_CONNECTED) {
OnSocketCanReceiveWithoutBlocking(aFd);
} else if (mConnectionStatus == SOCKET_IS_LISTENING) {
OnSocketCanAcceptWithoutBlocking(aFd);
} else {
NS_NOTREACHED("invalid connection state for reading");
}
}
void
DroidSocketImpl::OnSocketCanReceiveWithoutBlocking(int aFd)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mShuttingDownOnIOThread);
@ -563,12 +570,7 @@ DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
while (true) {
nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
ssize_t ret;
if (!mReadMsgForClientFd) {
ret = read(aFd, incoming->mData, incoming->mSize);
} else {
ret = ReadMsg(aFd, incoming->mData, incoming->mSize);
}
ssize_t ret = read(aFd, incoming->mData, incoming->mSize);
if (ret <= 0) {
if (ret == -1) {
@ -606,8 +608,113 @@ DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
MOZ_CRASH("We returned early");
}
class AcceptTask MOZ_FINAL : public DroidSocketImplTask
{
public:
AcceptTask(DroidSocketImpl* aDroidSocketImpl, int aFd)
: DroidSocketImplTask(aDroidSocketImpl)
, mFd(aFd)
{ }
void Run() MOZ_OVERRIDE
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!IsCanceled());
GetDroidSocketImpl()->Accept(mFd);
}
private:
int mFd;
};
class AcceptResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
{
public:
AcceptResultHandler(DroidSocketImpl* aImpl)
: mImpl(aImpl)
{
MOZ_ASSERT(mImpl);
}
void Accept(int aFd, const nsAString& aBdAddress,
int aConnectionStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (mImpl->IsShutdownOnMainThread()) {
BT_LOGD("mConsumer is null, aborting receive!");
return;
}
mImpl->mConsumer->SetAddress(aBdAddress);
XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new AcceptTask(mImpl, aFd));
}
void OnError(bt_status_t aStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("BluetoothSocketInterface::Accept failed: %d", (int)aStatus);
}
private:
DroidSocketImpl* mImpl;
};
class AcceptRunnable MOZ_FINAL : public nsRunnable
{
public:
AcceptRunnable(int aFd, DroidSocketImpl* aImpl)
: mFd(aFd)
, mImpl(aImpl)
{
MOZ_ASSERT(mImpl);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sBluetoothSocketInterface);
sBluetoothSocketInterface->Accept(mFd, new AcceptResultHandler(mImpl));
return NS_OK;
}
private:
int mFd;
DroidSocketImpl* mImpl;
};
void
DroidSocketImpl::OnSocketCanAcceptWithoutBlocking(int aFd)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mShuttingDownOnIOThread);
/* When a listening socket is ready for receiving data,
* we can call |Accept| on it.
*/
RemoveWatchers(READ_WATCHER);
nsRefPtr<AcceptRunnable> t = new AcceptRunnable(aFd, this);
NS_DispatchToMainThread(t);
}
void
DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
{
if (mConnectionStatus == SOCKET_IS_CONNECTED) {
OnSocketCanSendWithoutBlocking(aFd);
} else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
OnSocketCanConnectWithoutBlocking(aFd);
} else {
NS_NOTREACHED("invalid connection state for writing");
}
}
void
DroidSocketImpl::OnSocketCanSendWithoutBlocking(int aFd)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mShuttingDownOnIOThread);
@ -649,6 +756,28 @@ DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
}
}
void
DroidSocketImpl::OnSocketCanConnectWithoutBlocking(int aFd)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mShuttingDownOnIOThread);
/* We follow Posix behaviour here: Connect operations are
* complete once we can write to the connecting socket.
*/
mConnectionStatus = SOCKET_IS_CONNECTED;
nsRefPtr<OnSocketEventRunnable> r =
new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
NS_DispatchToMainThread(r);
AddWatchers(READ_WATCHER, true);
if (!mOutgoingQ.IsEmpty()) {
AddWatchers(WRITE_WATCHER, false);
}
}
BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
BluetoothSocketType aType,
bool aAuth,
@ -657,7 +786,6 @@ BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
, mImpl(nullptr)
, mAuth(aAuth)
, mEncrypt(aEncrypt)
, mReceivedSocketInfoLength(0)
{
MOZ_ASSERT(aObserver);
@ -681,35 +809,107 @@ BluetoothSocket::CloseDroidSocket()
new ShutdownSocketTask(mImpl));
mImpl = nullptr;
OnDisconnect();
NotifyDisconnect();
}
class ConnectResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
{
public:
ConnectResultHandler(DroidSocketImpl* aImpl)
: mImpl(aImpl)
{
MOZ_ASSERT(mImpl);
}
void Connect(int aFd, const nsAString& aBdAddress,
int aConnectionStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (!mImpl->IsShutdownOnMainThread()) {
mImpl->mConsumer->SetAddress(aBdAddress);
}
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
new SocketConnectTask(mImpl, aFd));
}
void OnError(bt_status_t aStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
BT_WARNING("Connect failed: %d", (int)aStatus);
}
private:
DroidSocketImpl* mImpl;
};
bool
BluetoothSocket::Connect(const nsAString& aDeviceAddress, int aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_FALSE(mImpl, false);
mIsServer = false;
mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this, aDeviceAddress,
aChannel, mAuth, mEncrypt);
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
new SocketConnectTask(mImpl));
bt_bdaddr_t remoteBdAddress;
StringToBdAddressType(aDeviceAddress, &remoteBdAddress);
// TODO: uuid as argument
sBluetoothSocketInterface->Connect(&remoteBdAddress,
BTSOCK_RFCOMM,
UUID_OBEX_OBJECT_PUSH,
aChannel,
(BTSOCK_FLAG_ENCRYPT * mEncrypt) |
(BTSOCK_FLAG_AUTH * mAuth),
new ConnectResultHandler(mImpl));
return true;
}
class ListenResultHandler MOZ_FINAL : public BluetoothSocketResultHandler
{
public:
ListenResultHandler(DroidSocketImpl* aImpl)
: mImpl(aImpl)
{
MOZ_ASSERT(mImpl);
}
void Listen(int aFd) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
new SocketListenTask(mImpl, aFd));
}
void OnError(bt_status_t aStatus) MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
BT_WARNING("Listen failed: %d", (int)aStatus);
}
private:
DroidSocketImpl* mImpl;
};
bool
BluetoothSocket::Listen(int aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_FALSE(mImpl, false);
mIsServer = true;
mImpl = new DroidSocketImpl(XRE_GetIOMessageLoop(), this, aChannel, mAuth,
mEncrypt);
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
new SocketListenTask(mImpl));
sBluetoothSocketInterface->Listen(BTSOCK_RFCOMM,
"OBEX Object Push",
UUID_OBEX_OBJECT_PUSH,
aChannel,
(BTSOCK_FLAG_ENCRYPT * mEncrypt) |
(BTSOCK_FLAG_AUTH * mAuth),
new ListenResultHandler(mImpl));
return true;
}
@ -725,64 +925,9 @@ BluetoothSocket::SendDroidSocketData(UnixSocketRawData* aData)
return true;
}
bool
BluetoothSocket::ReceiveSocketInfo(nsAutoPtr<UnixSocketRawData>& aMessage)
{
MOZ_ASSERT(NS_IsMainThread());
/**
* 2 socket info messages (20 bytes) to receive at the beginning:
* - 1st message: [channel:4]
* - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
*/
if (mReceivedSocketInfoLength >= TOTAL_SOCKET_INFO_LENGTH) {
// We've got both socket info messages
return false;
}
mReceivedSocketInfoLength += aMessage->mSize;
size_t offset = 0;
if (mReceivedSocketInfoLength == FIRST_SOCKET_INFO_MSG_LENGTH) {
// 1st message: [channel:4]
int32_t channel = ReadInt32(aMessage->mData, &offset);
BT_LOGR("channel %d", channel);
// If this is server socket, read header of next message for client fd
mImpl->mReadMsgForClientFd = mIsServer;
} else if (mReceivedSocketInfoLength == TOTAL_SOCKET_INFO_LENGTH) {
// 2nd message: [size:2][bd address:6][channel:4][connection status:4]
int16_t size = ReadInt16(aMessage->mData, &offset);
ReadBdAddress(aMessage->mData, &offset, mDeviceAddress);
int32_t channel = ReadInt32(aMessage->mData, &offset);
int32_t connectionStatus = ReadInt32(aMessage->mData, &offset);
BT_LOGR("size %d channel %d remote addr %s status %d",
size, channel, NS_ConvertUTF16toUTF8(mDeviceAddress).get(), connectionStatus);
if (connectionStatus != 0) {
OnConnectError();
return true;
}
if (mIsServer) {
mImpl->mReadMsgForClientFd = false;
// Connect client fd on IO thread
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
new SocketConnectClientFdTask(mImpl));
}
OnConnectSuccess();
}
return true;
}
void
BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
{
if (ReceiveSocketInfo(aMessage)) {
return;
}
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mObserver);
mObserver->ReceiveSocketData(this, aMessage);
@ -811,4 +956,3 @@ BluetoothSocket::OnDisconnect()
MOZ_ASSERT(mObserver);
mObserver->OnSocketDisconnect(this);
}

Просмотреть файл

@ -23,30 +23,8 @@ public:
bool aAuth,
bool aEncrypt);
/**
* Connect to remote server as a client.
*
* The steps are as following:
* 1) BluetoothSocket acquires fd from bluedroid, and creates
* a DroidSocketImpl to watch read/write of the fd.
* 2) DroidSocketImpl receives first 2 messages to get socket info.
* 3) Obex client session starts.
*/
bool Connect(const nsAString& aDeviceAddress, int aChannel);
/**
* Listen to incoming connection as a server.
*
* The steps are as following:
* 1) BluetoothSocket acquires fd from bluedroid, and creates
* a DroidSocketImpl to watch read of the fd. DroidSocketImpl
* receives the 1st message immediately.
* 2) When there's incoming connection, DroidSocketImpl receives
* 2nd message to get socket info and client fd.
* 3) DroidSocketImpl stops watching read of original fd and
* starts to watch read/write of client fd.
* 4) Obex server session starts.
*/
bool Listen(int aChannel);
inline void Disconnect()
@ -65,6 +43,11 @@ public:
aDeviceAddress = mDeviceAddress;
}
inline void SetAddress(const nsAString& aDeviceAddress)
{
mDeviceAddress = aDeviceAddress;
}
void CloseDroidSocket();
bool SendDroidSocketData(mozilla::ipc::UnixSocketRawData* aData);
@ -74,10 +57,6 @@ private:
nsString mDeviceAddress;
bool mAuth;
bool mEncrypt;
bool mIsServer;
int mReceivedSocketInfoLength;
bool ReceiveSocketInfo(nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage);
};
END_BLUETOOTH_NAMESPACE

Просмотреть файл

@ -561,6 +561,43 @@ function waitForAdapterAttributeChanged(aAdapter, aAttrName, aExpectedValue) {
return deferred.promise;
}
/**
* Wait for specified number of 'devicefound' events.
*
* Resolve if specified number of devices has been found. Never reject.
*
* Fulfill params: an array which contains BluetoothDeviceEvents that we
* received from the BluetoothDiscoveryHandle.
*
* @param aDiscoveryHandle
* A BluetoothDiscoveryHandle which is used to notify application of
* discovered remote bluetooth devices.
* @param aExpectedNumberOfDevices
* The number of remote devices we expect to discovery.
*
* @return A deferred promise.
*/
function waitForDevicesFound(aDiscoveryHandle, aExpectedNumberOfDevices) {
let deferred = Promise.defer();
ok(aDiscoveryHandle instanceof BluetoothDiscoveryHandle,
"discoveryHandle should be a BluetoothDiscoveryHandle");
let devicesArray = [];
aDiscoveryHandle.ondevicefound = function onDeviceFound(aEvent) {
ok(aEvent instanceof BluetoothDeviceEvent,
"aEvent should be a BluetoothDeviceEvent");
devicesArray.push(aEvent);
if (devicesArray.length >= aExpectedNumberOfDevices) {
aDiscoveryHandle.ondevicefound = null;
deferred.resolve(devicesArray);
}
};
return deferred.promise;
}
/**
* Flush permission settings and call |finish()|.
*/

Просмотреть файл

@ -6,3 +6,4 @@ qemu = false
[test_dom_BluetoothManager_API2.js]
[test_dom_BluetoothAdapter_enable_API2.js]
[test_dom_BluetoothAdapter_setters_API2.js]
[test_dom_BluetoothAdapter_discovery_API2.js]

Просмотреть файл

@ -0,0 +1,124 @@
/* 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/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify the discovery process of BluetoothAdapter.
// Testers have to put the B2G devices in an environment which is surrounded
// by N discoverable remote devices. To pass this test, the number N has to be
// greater or equals than EXPECTED_NUMBER_OF_REMOTE_DEVICES.
//
// Test Procedure:
// [0] Set Bluetooth permission and enable default adapter.
// [1] Start discovery and verify the correctness.
// [2] Attach event handler for 'ondevicefound'.
// [3] Stop discovery and verify the correctness.
// [4] Mark the BluetoothDiscoveryHandle from [1] as expired.
// [5] Start discovery and verify the correctness.
// [6] Wait for 'devicefound' events.
// [7] Stop discovery and verify the correctness.
// [8] Call 'startDiscovery' twice continuously.
// [9] Call 'stopDiscovery' twice continuously.
// [10] Clean up the event handler of [2].
//
// Test Coverage:
// - BluetoothAdapter.discovering
// - BluetoothAdapter.startDiscovery()
// - BluetoothAdapter.stopDiscovery()
// - BluetoothAdapter.onattributechanged()
// - BluetoothDiscoveryHandle.ondevicefound()
//
///////////////////////////////////////////////////////////////////////////////
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const EXPECTED_NUMBER_OF_REMOTE_DEVICES = 2;
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Checking adapter attributes ...");
is(aAdapter.state, "enabled", "adapter.state");
isnot(aAdapter.address, "", "adapter.address");
// Since adapter has just been re-enabled, these properties should be 'false'.
is(aAdapter.discovering, false, "adapter.discovering");
is(aAdapter.discoverable, false, "adapter.discoverable");
log("adapter.address: " + aAdapter.address);
log("adapter.name: " + aAdapter.name);
let discoveryHandle = null;
return Promise.resolve()
.then(function() {
log("[1] Start discovery and verify the correctness ... ");
let promises = [];
promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", true));
promises.push(aAdapter.startDiscovery());
return Promise.all(promises);
})
.then(function(aResults) {
log("[2] Attach event handler for 'ondevicefound' ... ");
discoveryHandle = aResults[1];
isHandleExpired = false;
discoveryHandle.ondevicefound = function onDeviceFound(aEvent) {
if (isHandleExpired) {
ok(false, "Expired BluetoothDiscoveryHandle received an event.");
}
};
})
.then(function() {
log("[3] Stop discovery and and verify the correctness ... ");
let promises = [];
if (aAdapter.discovering) {
promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", false));
}
promises.push(aAdapter.stopDiscovery());
return Promise.all(promises);
})
.then(function() {
log("[4] Mark the BluetoothDiscoveryHandle from [1] as expired ... ");
isHandleExpired = true;
})
.then(function() {
log("[5] Start discovery and verify the correctness ... ");
let promises = [];
promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", true));
promises.push(aAdapter.startDiscovery());
return Promise.all(promises);
})
.then(function(aResults) {
log("[6] Wait for 'devicefound' events ... ");
return waitForDevicesFound(aResults[1], EXPECTED_NUMBER_OF_REMOTE_DEVICES);
})
.then(function() {
log("[7] Stop discovery and and verify the correctness ... ");
let promises = [];
if (aAdapter.discovering) {
promises.push(waitForAdapterAttributeChanged(aAdapter, "discovering", false));
}
promises.push(aAdapter.stopDiscovery());
return Promise.all(promises);
})
.then(function() {
log("[8] Call 'startDiscovery' twice continuously ... ");
return aAdapter.startDiscovery()
.then(() => aAdapter.startDiscovery())
.then(() => ok(false, "Call startDiscovery() when adapter is discovering. - Fail"),
() => ok(true, "Call startDiscovery() when adapter is discovering. - Success"));
})
.then(function() {
log("[9] Call 'stopDiscovery' twice continuously ... ");
return aAdapter.stopDiscovery()
.then(() => aAdapter.stopDiscovery())
.then(() => ok(true, "Call stopDiscovery() when adapter isn't discovering. - Success"),
() => ok(false, "Call stopDiscovery() when adapter isn't discovering. - Fail"));
})
.then(function() {
log("[10] Clean up the event handler of [2] ... ");
if (discoveryHandle) {
discoveryHandle.ondevicefound = null;
}
});
});

Просмотреть файл

@ -36,6 +36,7 @@
#include "mozilla/Services.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsComponentManagerUtils.h"
#include "nsAboutProtocolUtils.h"
#include "nsContentUtils.h"
#include "nsCRTGlue.h"
#include "nsDirectoryServiceUtils.h"
@ -2013,6 +2014,64 @@ QuotaManager::GetInfoFromURI(nsIURI* aURI,
return NS_OK;
}
static nsresult
TryGetInfoForAboutURI(nsIPrincipal* aPrincipal,
nsACString& aGroup,
nsACString& aASCIIOrigin,
StoragePrivilege* aPrivilege,
PersistenceType* aDefaultPersistenceType)
{
NS_ASSERTION(aPrincipal, "Don't hand me a null principal!");
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
if (!uri) {
return NS_ERROR_NOT_AVAILABLE;
}
bool isAbout;
rv = uri->SchemeIs("about", &isAbout);
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && isAbout, NS_ERROR_FAILURE);
nsCOMPtr<nsIAboutModule> module;
rv = NS_GetAboutModule(uri, getter_AddRefs(module));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> inner = NS_GetInnermostURI(uri);
NS_ENSURE_TRUE(inner, NS_ERROR_FAILURE);
nsAutoString postfix;
rv = module->GetIndexedDBOriginPostfix(uri, postfix);
NS_ENSURE_SUCCESS(rv, rv);
nsCString origin;
if (DOMStringIsNull(postfix)) {
rv = inner->GetSpec(origin);
NS_ENSURE_SUCCESS(rv, rv);
} else {
nsAutoCString scheme;
rv = inner->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
origin = scheme + NS_LITERAL_CSTRING(":") + NS_ConvertUTF16toUTF8(postfix);
}
ToLowerCase(origin);
aGroup.Assign(origin);
aASCIIOrigin.Assign(origin);
if (aPrivilege) {
*aPrivilege = Content;
}
if (aDefaultPersistenceType) {
*aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
}
return NS_OK;
}
// static
nsresult
QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
@ -2024,6 +2083,14 @@ QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aPrincipal, "Don't hand me a null principal!");
if (aGroup && aASCIIOrigin) {
nsresult rv = TryGetInfoForAboutURI(aPrincipal, *aGroup, *aASCIIOrigin,
aPrivilege, aDefaultPersistenceType);
if (NS_SUCCEEDED(rv)) {
return NS_OK;
}
}
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
GetInfoForChrome(aGroup, aASCIIOrigin, aPrivilege, aDefaultPersistenceType);
return NS_OK;

Просмотреть файл

@ -25,6 +25,7 @@
#include "mozilla/FileUtils.h"
#include "mozilla/Hal.h"
#include "mozilla/StaticPtr.h"
#include "MozMtpServer.h"
#include "nsAutoPtr.h"
#include "nsMemory.h"
#include "nsString.h"
@ -35,6 +36,7 @@
#include "VolumeManager.h"
using namespace mozilla::hal;
USING_MTP_NAMESPACE
/**************************************************************************
*
@ -69,6 +71,7 @@ using namespace mozilla::hal;
#define ICS_SYS_USB_FUNCTIONS "/sys/devices/virtual/android_usb/android0/functions"
#define ICS_SYS_UMS_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mass_storage"
#define ICS_SYS_MTP_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mtp"
#define ICS_SYS_USB_STATE "/sys/devices/virtual/android_usb/android0/state"
#define USE_DEBUG 0
@ -229,6 +232,9 @@ public:
}
}
void StartMtpServer();
void StopMtpServer();
void UpdateState();
const char* ModeStr(int32_t aMode)
@ -347,6 +353,7 @@ private:
};
static StaticRefPtr<AutoMounter> sAutoMounter;
static StaticRefPtr<MozMtpServer> sMozMtpServer;
/***************************************************************************/
@ -398,6 +405,25 @@ AutoMounterResponseCallback::ResponseReceived(const VolumeCommand* aCommand)
}
}
void
AutoMounter::StartMtpServer()
{
if (sMozMtpServer) {
// Mtp Server is already running - nothing to do
return;
}
LOG("Starting MtpServer");
sMozMtpServer = new MozMtpServer();
sMozMtpServer->Run();
}
void
AutoMounter::StopMtpServer()
{
LOG("Stopping MtpServer");
sMozMtpServer = nullptr;
}
/***************************************************************************/
void
@ -441,22 +467,23 @@ AutoMounter::UpdateState()
bool umsAvail = false;
bool umsEnabled = false;
bool mtpAvail = false;
bool mtpEnabled = false;
if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) {
char functionsStr[60];
if (!ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
functionsStr[0] = '\0';
}
umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0);
if (umsAvail) {
char functionsStr[60];
if (ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
} else {
ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
umsEnabled = false;
}
} else {
umsEnabled = false;
umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
}
mtpAvail = (access(ICS_SYS_MTP_DIRECTORY, F_OK) == 0);
if (mtpAvail) {
mtpEnabled = strstr(functionsStr, "mtp") != nullptr;
}
} else {
umsAvail = ReadSysFile(GB_SYS_UMS_ENABLE, &umsEnabled);
}
bool usbCablePluggedIn = IsUsbCablePluggedIn();
@ -469,9 +496,19 @@ AutoMounter::UpdateState()
}
}
bool tryToShare = (umsAvail && umsEnabled && enabled && usbCablePluggedIn);
LOG("UpdateState: umsAvail:%d umsEnabled:%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
umsAvail, umsEnabled, mMode, usbCablePluggedIn, tryToShare);
bool tryToShare = (((umsAvail && umsEnabled) || (mtpAvail && mtpEnabled))
&& enabled && usbCablePluggedIn);
LOG("UpdateState: ums:%d%d mtp:%d%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
umsAvail, umsEnabled, mtpAvail, mtpEnabled, mMode, usbCablePluggedIn, tryToShare);
if (mtpAvail && mtpEnabled) {
if (enabled && usbCablePluggedIn) {
StartMtpServer();
} else {
StopMtpServer();
}
return;
}
bool filesOpen = false;
static unsigned filesOpenDelayCount = 0;
@ -902,6 +939,9 @@ AutoMounterUnmountVolume(const nsCString& aVolumeName)
void
ShutdownAutoMounter()
{
if (sAutoMounter) {
sAutoMounter->StopMtpServer();
}
sAutoMounterSetting = nullptr;
sUsbCableObserver = nullptr;

Просмотреть файл

@ -0,0 +1,44 @@
/* -*- 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_system_mozmtpcommon_h__
#define mozilla_system_mozmtpcommon_h__
#include "mozilla/Types.h"
#include <android/log.h>
#define MTP_LOG(msg, ...) \
__android_log_print(ANDROID_LOG_INFO, "MozMtp", \
"%s: " msg, __FUNCTION__, ##__VA_ARGS__) \
#define MTP_ERR(msg, ...) \
__android_log_print(ANDROID_LOG_ERROR, "MozMtp", \
"%s: " msg, __FUNCTION__, ##__VA_ARGS__) \
#define BEGIN_MTP_NAMESPACE \
namespace mozilla { namespace system { namespace mtp {
#define END_MTP_NAMESPACE \
} /* namespace mtp */ } /* namespace system */ } /* namespace mozilla */
#define USING_MTP_NAMESPACE \
using namespace mozilla::system::mtp;
namespace android {
class MOZ_EXPORT MtpServer;
class MOZ_EXPORT MtpStorage;
class MOZ_EXPORT MtpDatabase;
class MOZ_EXPORT MtpDataPacket;
class MOZ_EXPORT MtpProperty;
}
#include <mtp.h>
#include <MtpDatabase.h>
#include <MtpObjectInfo.h>
#include <MtpProperty.h>
#include <MtpServer.h>
#include <MtpStorage.h>
#include <MtpTypes.h>
#endif // mozilla_system_mtpcommon_h__

Просмотреть файл

@ -0,0 +1,792 @@
/* -*- 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 "MozMtpDatabase.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Scoped.h"
#include "nsIFile.h"
#include "nsPrintfCString.h"
#include "prio.h"
#include <dirent.h>
#include <libgen.h>
using namespace android;
using namespace mozilla;
namespace mozilla {
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCloseDir, PRDir, PR_CloseDir)
}
BEGIN_MTP_NAMESPACE
static const char *
ObjectPropertyAsStr(MtpObjectProperty aProperty)
{
switch (aProperty) {
case MTP_PROPERTY_STORAGE_ID: return "MTP_PROPERTY_STORAGE_ID";
case MTP_PROPERTY_OBJECT_FORMAT: return "MTP_PROPERTY_OBJECT_FORMAT";
case MTP_PROPERTY_OBJECT_SIZE: return "MTP_PROPERTY_OBJECT_SIZE";
case MTP_PROPERTY_WIDTH: return "MTP_PROPERTY_WIDTH";
case MTP_PROPERTY_HEIGHT: return "MTP_PROPERTY_HEIGHT";
case MTP_PROPERTY_IMAGE_BIT_DEPTH: return "MTP_PROPERTY_IMAGE_BIT_DEPTH";
case MTP_PROPERTY_DISPLAY_NAME: return "MTP_PROPERTY_DISPLAY_NAME";
}
return "MTP_PROPERTY_???";
}
MozMtpDatabase::MozMtpDatabase(const char *aDir)
{
MTP_LOG("");
// We use the index into the array as the handle. Since zero isn't a valid
// index, we stick a dummy entry there.
RefPtr<DbEntry> dummy;
mDb.AppendElement(dummy);
ReadVolume("sdcard", aDir);
}
//virtual
MozMtpDatabase::~MozMtpDatabase()
{
MTP_LOG("");
}
void
MozMtpDatabase::AddEntry(DbEntry *entry)
{
entry->mHandle = GetNextHandle();
MOZ_ASSERT(mDb.Length() == entry->mHandle);
mDb.AppendElement(entry);
MTP_LOG("AddEntry: Handle: 0x%08x Parent: 0x%08x Path:'%s'",
entry->mHandle, entry->mParent, entry->mPath.get());
}
TemporaryRef<MozMtpDatabase::DbEntry>
MozMtpDatabase::GetEntry(MtpObjectHandle aHandle)
{
RefPtr<DbEntry> entry;
if (aHandle > 0 && aHandle < mDb.Length()) {
entry = mDb[aHandle];
}
return entry;
}
void
MozMtpDatabase::RemoveEntry(MtpObjectHandle aHandle)
{
if (aHandle > 0 && aHandle < mDb.Length()) {
mDb[aHandle] = nullptr;
}
}
nsCString
MozMtpDatabase::BaseName(const nsCString& path)
{
nsCOMPtr<nsIFile> file;
NS_NewNativeLocalFile(path, false, getter_AddRefs(file));
if (file) {
nsCString leafName;
file->GetNativeLeafName(leafName);
return leafName;
}
return path;
}
void
MozMtpDatabase::ParseDirectory(const char *aDir, MtpObjectHandle aParent)
{
ScopedCloseDir dir;
if (!(dir = PR_OpenDir(aDir))) {
MTP_ERR("Unable to open directory '%s'", aDir);
return;
}
PRDirEntry* dirEntry;
while ((dirEntry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
nsPrintfCString filename("%s/%s", aDir, dirEntry->name);
PRFileInfo64 fileInfo;
if (PR_GetFileInfo64(filename.get(), &fileInfo) != PR_SUCCESS) {
MTP_ERR("Unable to retrieve file information for '%s'", filename.get());
continue;
}
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = MTP_STORAGE_FIXED_RAM;
entry->mParent = aParent;
entry->mObjectName = dirEntry->name;
entry->mDisplayName = dirEntry->name;
entry->mPath = filename;
entry->mDateCreated = fileInfo.creationTime;
entry->mDateModified = fileInfo.modifyTime;
if (fileInfo.type == PR_FILE_FILE) {
entry->mObjectFormat = MTP_FORMAT_DEFINED;
//TODO: Check how 64-bit filesize are dealt with
entry->mObjectSize = fileInfo.size;
AddEntry(entry);
} else if (fileInfo.type == PR_FILE_DIRECTORY) {
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
entry->mObjectSize = 0;
AddEntry(entry);
ParseDirectory(filename.get(), entry->mHandle);
}
}
}
void
MozMtpDatabase::ReadVolume(const char *volumeName, const char *aDir)
{
//TODO: Add an assert re thread being run on
PRFileInfo fileInfo;
if (PR_GetFileInfo(aDir, &fileInfo) != PR_SUCCESS) {
MTP_ERR("'%s' doesn't exist", aDir);
return;
}
if (fileInfo.type != PR_FILE_DIRECTORY) {
MTP_ERR("'%s' isn't a directory", aDir);
return;
}
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = MTP_STORAGE_FIXED_RAM;
entry->mParent = MTP_PARENT_ROOT;
entry->mObjectName = volumeName;
entry->mDisplayName = volumeName;
entry->mPath = aDir;
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
entry->mObjectSize = 0;
AddEntry(entry);
ParseDirectory(aDir, entry->mHandle);
}
// called from SendObjectInfo to reserve a database entry for the incoming file
//virtual
MtpObjectHandle
MozMtpDatabase::beginSendObject(const char* aPath,
MtpObjectFormat aFormat,
MtpObjectHandle aParent,
MtpStorageID aStorageID,
uint64_t aSize,
time_t aModified)
{
if (!aParent) {
MTP_LOG("aParent is NULL");
return kInvalidObjectHandle;
}
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = aStorageID;
entry->mParent = aParent;
entry->mPath = aPath;
entry->mObjectName = BaseName(entry->mPath);
entry->mDisplayName = entry->mObjectName;
entry->mObjectFormat = aFormat;
entry->mObjectSize = aSize;
AddEntry(entry);
MTP_LOG("Handle: 0x%08x Parent: 0x%08x Path: '%s'", entry->mHandle, aParent, aPath);
return entry->mHandle;
}
// called to report success or failure of the SendObject file transfer
// success should signal a notification of the new object's creation,
// failure should remove the database entry created in beginSendObject
//virtual
void
MozMtpDatabase::endSendObject(const char* aPath,
MtpObjectHandle aHandle,
MtpObjectFormat aFormat,
bool succeeded)
{
MTP_LOG("Handle: 0x%08x Path: '%s'", aHandle, aPath);
if (!succeeded) {
RemoveEntry(aHandle);
}
}
//virtual
MtpObjectHandleList*
MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
{
MTP_LOG("StorageID: 0x%08x Format: 0x%04x Parent: 0x%08x",
aStorageID, aFormat, aParent);
//TODO: Optimize
ScopedDeletePtr<MtpObjectHandleList> list;
list = new MtpObjectHandleList();
DbArray::size_type numEntries = mDb.Length();
DbArray::index_type entryIndex;
for (entryIndex = 1; entryIndex < numEntries; entryIndex++) {
RefPtr<DbEntry> entry = mDb[entryIndex];
if (entry->mParent == aParent) {
list->push(entry->mHandle);
}
}
return list.forget();
}
//virtual
int
MozMtpDatabase::getNumObjects(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
{
MTP_LOG("");
return mDb.Length() - 1;
}
//virtual
MtpObjectFormatList*
MozMtpDatabase::getSupportedPlaybackFormats()
{
static const uint16_t init_data[] = {MTP_FORMAT_PNG};
MtpObjectFormatList *list = new MtpObjectFormatList();
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
MTP_LOG("returning MTP_FORMAT_PNG");
return list;
}
//virtual
MtpObjectFormatList*
MozMtpDatabase::getSupportedCaptureFormats()
{
static const uint16_t init_data[] = {MTP_FORMAT_ASSOCIATION, MTP_FORMAT_PNG};
MtpObjectFormatList *list = new MtpObjectFormatList();
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
MTP_LOG("returning MTP_FORMAT_PNG");
return list;
}
static const MtpObjectProperty sSupportedObjectProperties[] =
{
MTP_PROPERTY_STORAGE_ID,
MTP_PROPERTY_PARENT_OBJECT,
MTP_PROPERTY_OBJECT_FORMAT,
MTP_PROPERTY_OBJECT_SIZE,
MTP_PROPERTY_OBJECT_FILE_NAME, // just the filename - no directory
MTP_PROPERTY_PROTECTION_STATUS, // UINT16 - always 0
MTP_PROPERTY_DATE_MODIFIED,
MTP_PROPERTY_DATE_ADDED,
};
//virtual
MtpObjectPropertyList*
MozMtpDatabase::getSupportedObjectProperties(MtpObjectFormat aFormat)
{
MTP_LOG("");
MtpObjectPropertyList *list = new MtpObjectPropertyList();
list->appendArray(sSupportedObjectProperties,
MOZ_ARRAY_LENGTH(sSupportedObjectProperties));
return list;
}
//virtual
MtpDevicePropertyList*
MozMtpDatabase::getSupportedDeviceProperties()
{
MTP_LOG("");
static const uint16_t init_data[] = { MTP_DEVICE_PROPERTY_UNDEFINED };
MtpDevicePropertyList *list = new MtpDevicePropertyList();
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
return list;
}
//virtual
MtpResponseCode
MozMtpDatabase::getObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
MTP_ERR("Invalid Handle: 0x%08x", aHandle);
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
MTP_LOG("Handle: 0x%08x '%s' Property: %s 0x%08x",
aHandle, entry->mDisplayName.get(), ObjectPropertyAsStr(aProperty), aProperty);
switch (aProperty)
{
case MTP_PROPERTY_STORAGE_ID: aPacket.putUInt32(entry->mStorageID); break;
case MTP_PROPERTY_PARENT_OBJECT: aPacket.putUInt32(entry->mParent); break;
case MTP_PROPERTY_OBJECT_FORMAT: aPacket.putUInt32(entry->mObjectFormat); break;
case MTP_PROPERTY_OBJECT_SIZE: aPacket.putUInt32(entry->mObjectSize); break;
case MTP_PROPERTY_DISPLAY_NAME: aPacket.putString(entry->mDisplayName.get()); break;
default:
MTP_LOG("Invalid Property: 0x%08x", aProperty);
return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
}
return MTP_RESPONSE_OK;
}
//virtual
MtpResponseCode
MozMtpDatabase::setObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
//virtual
MtpResponseCode
MozMtpDatabase::getDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("(GENERAL ERROR)");
return MTP_RESPONSE_GENERAL_ERROR;
}
//virtual
MtpResponseCode
MozMtpDatabase::setDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("(NOT SUPPORTED)");
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
//virtual
MtpResponseCode
MozMtpDatabase::resetDeviceProperty(MtpDeviceProperty aProperty)
{
MTP_LOG("(NOT SUPPORTED)");
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
void
MozMtpDatabase::QueryEntries(MozMtpDatabase::MatchType aMatchType,
uint32_t aMatchField1,
uint32_t aMatchField2,
DbArray &result)
{
DbArray::size_type numEntries = mDb.Length();
DbArray::index_type entryIdx;
RefPtr<DbEntry> entry;
result.Clear();
switch (aMatchType) {
case MatchAll:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
if (mDb[entryIdx]) {
result.AppendElement(mDb[entryIdx]);
}
}
break;
case MatchHandle:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mHandle == aMatchField1) {
result.AppendElement(entry);
// Handles are unique - return the one that we found.
return;
}
}
break;
case MatchParent:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mParent == aMatchField1) {
result.AppendElement(entry);
}
}
break;
case MatchFormat:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mObjectFormat == aMatchField1) {
result.AppendElement(entry);
}
}
break;
case MatchHandleFormat:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mHandle == aMatchField1) {
if (entry->mObjectFormat == aMatchField2) {
result.AppendElement(entry);
}
// Only 1 entry can match my aHandle. So we can return early.
return;
}
}
break;
case MatchParentFormat:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mParent == aMatchField1 && entry->mObjectFormat == aMatchField2) {
result.AppendElement(entry);
}
}
break;
default:
MOZ_ASSERT(!"Invalid MatchType");
}
}
//virtual
MtpResponseCode
MozMtpDatabase::getObjectPropertyList(MtpObjectHandle aHandle,
uint32_t aFormat,
uint32_t aProperty,
int aGroupCode,
int aDepth,
MtpDataPacket& aPacket)
{
MTP_LOG("Handle: 0x%08x Format: 0x%08x aProperty: 0x%08x aGroupCode: %d aDepth %d (NOT SUPPORTED)",
aHandle, aFormat, aProperty, aGroupCode, aDepth);
if (aDepth > 1) {
return MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED;
}
if (aGroupCode != 0) {
return MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED;
}
MatchType matchType = MatchAll;
uint32_t matchField1 = 0;
uint32_t matchField2 = 0;
// aHandle == 0 implies all objects at the root level
// further specificed by aFormat and/or aDepth
if (aFormat == 0) {
if (aHandle == 0xffffffff) {
// select all objects
matchType = MatchAll;
} else {
if (aDepth == 1) {
// select objects whose Parent matches aHandle
matchType = MatchParent;
matchField1 = aHandle;
} else {
// select object whose handle matches aHandle
matchType = MatchHandle;
matchField1 = aHandle;
}
}
} else {
if (aHandle == 0xffffffff) {
// select all objects whose format matches aFormat
matchType = MatchFormat;
matchField1 = aFormat;
} else {
if (aDepth == 1) {
// select objects whose Parent is aHandle and format matches aFormat
matchType = MatchParentFormat;
matchField1 = aHandle;
matchField2 = aFormat;
} else {
// select objects whose handle is aHandle and format matches aFormat
matchType = MatchHandleFormat;
matchField1 = aHandle;
matchField2 = aFormat;
}
}
}
DbArray result;
QueryEntries(matchType, matchField1, matchField2, result);
const MtpObjectProperty *objectPropertyList;
size_t numObjectProperties = 0;
MtpObjectProperty objectProperty;
if (aProperty == 0xffffffff) {
// return all supported properties
numObjectProperties = MOZ_ARRAY_LENGTH(sSupportedObjectProperties);
objectPropertyList = sSupportedObjectProperties;
} else {
// return property indicated by aProperty
numObjectProperties = 1;
objectProperty = aProperty;
objectPropertyList = &objectProperty;
}
DbArray::size_type numEntries = result.Length();
DbArray::index_type entryIdx;
aPacket.putUInt32(numEntries);
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
RefPtr<DbEntry> entry = result[entryIdx];
for (size_t propertyIdx = 0; propertyIdx < numObjectProperties; propertyIdx++) {
aPacket.putUInt32(entry->mHandle);
MtpObjectProperty prop = objectPropertyList[propertyIdx];
aPacket.putUInt16(prop);
switch (prop) {
case MTP_PROPERTY_STORAGE_ID:
aPacket.putUInt16(MTP_TYPE_UINT32);
aPacket.putUInt32(entry->mStorageID);
break;
case MTP_PROPERTY_PARENT_OBJECT:
aPacket.putUInt16(MTP_TYPE_UINT32);
aPacket.putUInt32(entry->mParent);
break;
case MTP_PROPERTY_OBJECT_FORMAT:
aPacket.putUInt16(MTP_TYPE_UINT16);
aPacket.putUInt16(entry->mObjectFormat);
break;
case MTP_PROPERTY_OBJECT_SIZE:
aPacket.putUInt16(MTP_TYPE_UINT64);
aPacket.putUInt64(entry->mObjectSize);
break;
case MTP_PROPERTY_OBJECT_FILE_NAME:
aPacket.putUInt16(MTP_TYPE_STR);
aPacket.putString(entry->mObjectName.get());
break;
case MTP_PROPERTY_PROTECTION_STATUS:
aPacket.putUInt16(MTP_TYPE_UINT16);
aPacket.putUInt16(0); // 0 = No Protection
break;
case MTP_PROPERTY_DATE_MODIFIED: {
aPacket.putUInt16(MTP_TYPE_STR);
PRExplodedTime explodedTime;
PR_ExplodeTime(entry->mDateModified, PR_LocalTimeParameters, &explodedTime);
char dateStr[20];
PR_FormatTime(dateStr, sizeof(dateStr), "%Y%m%dT%H%M%S", &explodedTime);
aPacket.putString(dateStr);
break;
}
case MTP_PROPERTY_DATE_ADDED: {
aPacket.putUInt16(MTP_TYPE_STR);
PRExplodedTime explodedTime;
PR_ExplodeTime(entry->mDateCreated, PR_LocalTimeParameters, &explodedTime);
char dateStr[20];
PR_FormatTime(dateStr, sizeof(dateStr), "%Y%m%dT%H%M%S", &explodedTime);
aPacket.putString(dateStr);
break;
}
default:
MTP_ERR("Unrecognized property code: %u", prop);
return MTP_RESPONSE_GENERAL_ERROR;
}
}
}
return MTP_RESPONSE_OK;
}
//virtual
MtpResponseCode
MozMtpDatabase::getObjectInfo(MtpObjectHandle aHandle,
MtpObjectInfo& aInfo)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
MTP_ERR("Handle 0x%08x is invalid", aHandle);
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
MTP_LOG("Handle: 0x%08x Display:'%s' Object:'%s'", aHandle, entry->mDisplayName.get(), entry->mObjectName.get());
aInfo.mHandle = aHandle;
aInfo.mStorageID = entry->mStorageID;
aInfo.mFormat = entry->mObjectFormat;
aInfo.mProtectionStatus = 0x0;
aInfo.mCompressedSize = 0;
aInfo.mThumbFormat = entry->mObjectFormat;
aInfo.mThumbCompressedSize = 20*20*4;
aInfo.mThumbPixWidth = 20;
aInfo.mThumbPixHeight =20;
aInfo.mImagePixWidth = 20;
aInfo.mImagePixHeight = 20;
aInfo.mImagePixDepth = 4;
aInfo.mParent = entry->mParent;
aInfo.mAssociationType = 0;
aInfo.mAssociationDesc = 0;
aInfo.mSequenceNumber = 0;
aInfo.mName = ::strdup(entry->mObjectName.get());
aInfo.mDateCreated = 0;
aInfo.mDateModified = 0;
aInfo.mKeywords = ::strdup("fxos,touch");
return MTP_RESPONSE_OK;
}
//virtual
void*
MozMtpDatabase::getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize)
{
MTP_LOG("Handle: 0x%08x (returning nullptr)", aHandle);
aOutThumbSize = 0;
return nullptr;
}
//virtual
MtpResponseCode
MozMtpDatabase::getObjectFilePath(MtpObjectHandle aHandle,
MtpString& aOutFilePath,
int64_t& aOutFileLength,
MtpObjectFormat& aOutFormat)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
MTP_ERR("Handle 0x%08x is invalid", aHandle);
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
MTP_LOG("Handle: 0x%08x FilePath: '%s'", aHandle, entry->mPath.get());
aOutFilePath = entry->mPath.get();
aOutFileLength = entry->mObjectSize;
aOutFormat = entry->mObjectFormat;
return MTP_RESPONSE_OK;
}
//virtual
MtpResponseCode
MozMtpDatabase::deleteFile(MtpObjectHandle aHandle)
{
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
//TODO
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
#if 0
//virtual
MtpResponseCode
MozMtpDatabase::moveFile(MtpObjectHandle aHandle, MtpObjectHandle aNewParent)
{
MTP_LOG("Handle: 0x%08x NewParent: 0x%08x", aHandle, aNewParent);
// change parent
return MTP_RESPONSE_OK
}
//virtual
MtpResponseCode
MozMtpDatabase::copyFile(MtpObjectHandle aHandle, MtpObjectHandle aNewParent)
{
MTP_LOG("Handle: 0x%08x NewParent: 0x%08x", aHandle, aNewParent);
// duplicate DbEntry
// change parent
return MTP_RESPONSE_OK
}
#endif
//virtual
MtpObjectHandleList*
MozMtpDatabase::getObjectReferences(MtpObjectHandle aHandle)
{
MTP_LOG("Handle: 0x%08x (returning nullptr)", aHandle);
return nullptr;
}
//virtual
MtpResponseCode
MozMtpDatabase::setObjectReferences(MtpObjectHandle aHandle,
MtpObjectHandleList* aReferences)
{
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
//virtual
MtpProperty*
MozMtpDatabase::getObjectPropertyDesc(MtpObjectProperty aProperty,
MtpObjectFormat aFormat)
{
MTP_LOG("Property: %s 0x%08x", ObjectPropertyAsStr(aProperty), aProperty);
// TODO: Perhaps Filesize should be 64-bit?
MtpProperty* result = nullptr;
switch (aProperty)
{
case MTP_PROPERTY_STORAGE_ID: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_OBJECT_FORMAT: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_OBJECT_SIZE: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_WIDTH: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_HEIGHT: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_IMAGE_BIT_DEPTH: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_DISPLAY_NAME: result = new MtpProperty(aProperty, MTP_TYPE_STR); break;
default:
break;
}
return result;
}
//virtual
MtpProperty*
MozMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty aProperty)
{
MTP_LOG("(returning MTP_DEVICE_PROPERTY_UNDEFINED)");
return new MtpProperty(MTP_DEVICE_PROPERTY_UNDEFINED, MTP_TYPE_UNDEFINED);
}
//virtual
void
MozMtpDatabase::sessionStarted()
{
MTP_LOG("");
}
//virtual
void
MozMtpDatabase::sessionEnded()
{
MTP_LOG("");
}
END_MTP_NAMESPACE

Просмотреть файл

@ -0,0 +1,161 @@
/* -*- 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_system_mozmtpdatabase_h__
#define mozilla_system_mozmtpdatabase_h__
#include "MozMtpCommon.h"
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsTArray.h"
BEGIN_MTP_NAMESPACE // mozilla::system::mtp
using namespace android;
class MozMtpDatabase : public MtpDatabase
{
public:
MozMtpDatabase(const char *aDir);
virtual ~MozMtpDatabase();
// called from SendObjectInfo to reserve a database entry for the incoming file
virtual MtpObjectHandle beginSendObject(const char* aPath,
MtpObjectFormat aFormat,
MtpObjectHandle aParent,
MtpStorageID aStorageID,
uint64_t aSize,
time_t aModified);
// called to report success or failure of the SendObject file transfer
// success should signal a notification of the new object's creation,
// failure should remove the database entry created in beginSendObject
virtual void endSendObject(const char* aPath,
MtpObjectHandle aHandle,
MtpObjectFormat aFormat,
bool aSucceeded);
virtual MtpObjectHandleList* getObjectList(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent);
virtual int getNumObjects(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent);
virtual MtpObjectFormatList* getSupportedPlaybackFormats();
virtual MtpObjectFormatList* getSupportedCaptureFormats();
virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat aFormat);
virtual MtpDevicePropertyList* getSupportedDeviceProperties();
virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty aProperty);
virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle aHandle,
uint32_t aFormat,
uint32_t aProperty,
int aGroupCode,
int aDepth,
MtpDataPacket& aPacket);
virtual MtpResponseCode getObjectInfo(MtpObjectHandle aHandle,
MtpObjectInfo& aInfo);
virtual void* getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize);
virtual MtpResponseCode getObjectFilePath(MtpObjectHandle aHandle,
MtpString& aOutFilePath,
int64_t& aOutFileLength,
MtpObjectFormat& aOutFormat);
virtual MtpResponseCode deleteFile(MtpObjectHandle aHandle);
virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle aHandle);
virtual MtpResponseCode setObjectReferences(MtpObjectHandle aHandle,
MtpObjectHandleList* aReferences);
virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty aProperty,
MtpObjectFormat aFormat);
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty aProperty);
virtual void sessionStarted();
virtual void sessionEnded();
private:
struct DbEntry
{
NS_INLINE_DECL_REFCOUNTING(DbEntry)
MtpObjectHandle mHandle; // uint32_t
MtpStorageID mStorageID; // uint32_t
nsCString mObjectName;
MtpObjectFormat mObjectFormat; // uint16_t
MtpObjectHandle mParent; // uint32_t
uint64_t mObjectSize;
nsCString mDisplayName;
nsCString mPath;
PRTime mDateCreated;
PRTime mDateModified;
};
typedef nsTArray<mozilla::RefPtr<DbEntry> > DbArray;
DbArray mDb;
enum MatchType
{
MatchAll,
MatchHandle,
MatchParent,
MatchFormat,
MatchHandleFormat,
MatchParentFormat,
};
void AddEntry(DbEntry *aEntry);
mozilla::TemporaryRef<DbEntry> GetEntry(MtpObjectHandle aHandle);
void RemoveEntry(MtpObjectHandle aHandle);
void QueryEntries(MatchType aMatchType, uint32_t aMatchField1,
uint32_t aMatchField2, DbArray& aResult);
nsCString BaseName(const nsCString& aPath);
MtpObjectHandle GetNextHandle()
{
return mDb.Length();
}
void ParseDirectory(const char *aDir, MtpObjectHandle aParent);
void ReadVolume(const char *aVolumeName, const char *aDir);
};
END_MTP_NAMESPACE
#endif // mozilla_system_mozmtpdatabase_h__

Просмотреть файл

@ -0,0 +1,80 @@
/* -*- 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 "MozMtpServer.h"
#include "MozMtpDatabase.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <cutils/properties.h>
#include "mozilla/FileUtils.h"
#include "mozilla/Scoped.h"
#include "nsThreadUtils.h"
using namespace android;
using namespace mozilla;
USING_MTP_NAMESPACE
class MtpServerRunnable : public nsRunnable
{
public:
nsresult Run()
{
const char *mtpUsbFilename = "/dev/mtp_usb";
const char *productName = "FirefoxOS";
const char *storageDir = "/storage/sdcard";
mFd = open(mtpUsbFilename, O_RDWR);
if (mFd.get() < 0) {
MTP_LOG("open of '%s' failed", mtpUsbFilename);
return NS_OK;
}
MTP_LOG("MozMtpServer open done, fd: %d. Start reading.", mFd.get());
ScopedDeletePtr<MozMtpDatabase> database;
ScopedDeletePtr<MtpServer> server;
ScopedDeletePtr<MtpStorage> storage;
database = new MozMtpDatabase(storageDir);
server = new MtpServer(mFd.get(), database, false, 1023, 0664, 0775);
storage = new MtpStorage(MTP_STORAGE_FIXED_RAM, // id
storageDir, // filePath
productName, // description
100uLL * 1024uLL * 1024uLL, // reserveSpace
false, // removable
2uLL * 1024uLL * 1024uLL * 1024uLL); // maxFileSize
server->addStorage(storage);
MTP_LOG("MozMtpServer started");
server->run();
MTP_LOG("MozMtpServer finished");
return NS_OK;
}
private:
ScopedClose mFd;
};
void
MozMtpServer::Run()
{
nsresult rv = NS_NewNamedThread("MtpServer", getter_AddRefs(mServerThread));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
MOZ_ASSERT(mServerThread);
mServerThread->Dispatch(new MtpServerRunnable(), 0);
}

Просмотреть файл

@ -0,0 +1,32 @@
/* -*- 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_system_mozmtpserver_h__
#define mozilla_system_mozmtpserver_h__
#include "MozMtpCommon.h"
#include "nsCOMPtr.h"
#include "nsIThread.h"
BEGIN_MTP_NAMESPACE
class MozMtpServer
{
public:
NS_INLINE_DECL_REFCOUNTING(MozMtpServer)
void Run();
private:
nsCOMPtr<nsIThread> mServerThread;
};
END_MTP_NAMESPACE
#endif // mozilla_system_mozmtpserver_h__

Просмотреть файл

@ -41,6 +41,8 @@ SOURCES += [
'AutoMounter.cpp',
'AutoMounterSetting.cpp',
'GonkGPSGeolocationProvider.cpp',
'MozMtpDatabase.cpp',
'MozMtpServer.cpp',
'NetworkUtils.cpp',
'NetworkWorker.cpp',
'nsVolume.cpp',
@ -57,6 +59,11 @@ SOURCES += [
'VolumeServiceTest.cpp',
]
if CONFIG['ANDROID_VERSION'] >= '17':
CXXFLAGS += ['-I%s/frameworks/av/media/mtp' % CONFIG['ANDROID_SOURCE']]
else:
CXXFLAGS += ['-I%s/frameworks/base/media/mtp' % CONFIG['ANDROID_SOURCE']]
if CONFIG['ENABLE_TESTS']:
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']

Просмотреть файл

@ -1146,6 +1146,7 @@ var WifiManager = (function() {
createWaitForDriverReadyTimer(doStartWifiTethering);
});
} else {
cancelWifiHotspotStatusTimer();
gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
configuration, function(result) {
// Should we fire a dom event if we fail to set wifi tethering ?

Просмотреть файл

@ -5,6 +5,7 @@
#include "nsAboutBlank.h"
#include "nsStringStream.h"
#include "nsDOMString.h"
#include "nsNetUtil.h"
NS_IMPL_ISUPPORTS(nsAboutBlank, nsIAboutModule)
@ -36,6 +37,13 @@ nsAboutBlank::GetURIFlags(nsIURI *aURI, uint32_t *result)
return NS_OK;
}
NS_IMETHODIMP
nsAboutBlank::GetIndexedDBOriginPostfix(nsIURI *aURI, nsAString &result)
{
SetDOMStringToNull(result);
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsAboutBlank::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{

Просмотреть файл

@ -10,6 +10,7 @@
#include "nsAboutBloat.h"
#include "nsStringStream.h"
#include "nsDOMString.h"
#include "nsIURI.h"
#include "nsCOMPtr.h"
#include "nsNetUtil.h"
@ -124,6 +125,13 @@ nsAboutBloat::GetURIFlags(nsIURI *aURI, uint32_t *result)
return NS_OK;
}
NS_IMETHODIMP
nsAboutBloat::GetIndexedDBOriginPostfix(nsIURI *aURI, nsAString &result)
{
SetDOMStringToNull(result);
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsAboutBloat::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{

Просмотреть файл

@ -12,6 +12,7 @@
#include "nsEscape.h"
#include "nsAboutProtocolUtils.h"
#include "nsPrintfCString.h"
#include "nsDOMString.h"
#include "nsICacheStorageService.h"
#include "nsICacheStorage.h"
@ -485,6 +486,13 @@ nsAboutCache::GetURIFlags(nsIURI *aURI, uint32_t *result)
return NS_OK;
}
NS_IMETHODIMP
nsAboutCache::GetIndexedDBOriginPostfix(nsIURI *aURI, nsAString &result)
{
SetDOMStringToNull(result);
return NS_ERROR_NOT_IMPLEMENTED;
}
// static
nsresult
nsAboutCache::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)

Просмотреть файл

@ -7,6 +7,7 @@
#include "nsAboutCache.h"
#include "nsICacheStorage.h"
#include "CacheObserver.h"
#include "nsDOMString.h"
#include "nsNetUtil.h"
#include "prprf.h"
#include "nsEscape.h"
@ -108,6 +109,13 @@ nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result)
return NS_OK;
}
NS_IMETHODIMP
nsAboutCacheEntry::GetIndexedDBOriginPostfix(nsIURI *aURI, nsAString &result)
{
SetDOMStringToNull(result);
return NS_ERROR_NOT_IMPLEMENTED;
}
//-----------------------------------------------------------------------------
// nsAboutCacheEntry

Просмотреть файл

@ -8,7 +8,7 @@
interface nsIURI;
interface nsIChannel;
[scriptable, uuid(9575693c-60d9-4332-b6b8-6c29289339cb)]
[scriptable, uuid(1d5992c3-28b0-4ec1-9dbb-f5fde7f72199)]
interface nsIAboutModule : nsISupports
{
/**
@ -41,12 +41,24 @@ interface nsIAboutModule : nsISupports
*/
const unsigned long HIDE_FROM_ABOUTABOUT = (1 << 2);
/**
* A flag that indicates whether this about: URI wants Indexed DB enabled.
*/
const unsigned long ENABLE_INDEXED_DB = (1 << 3);
/**
* A method to get the flags that apply to a given about: URI. The URI
* passed in is guaranteed to be one of the URIs that this module
* registered to deal with.
*/
unsigned long getURIFlags(in nsIURI aURI);
/**
* Returns the Indexed DB origin's postfix used for the given about: URI.
* If the postfix returned is null then the URI's path (e.g. "home" for
* about:home) will be used to construct the origin.
*/
DOMString getIndexedDBOriginPostfix(in nsIURI aURI);
};
%{C++

Просмотреть файл

@ -776,8 +776,12 @@ BoxModelHighlighter.prototype = Heritage.extend(XULBasedHighlighter.prototype, {
if (this._nodeNeedsHighlighting()) {
for (let boxType in this._boxModelNodes) {
let {p1, p2, p3, p4} =
this.layoutHelpers.getAdjustedQuads(this.currentNode, boxType);
let quads = this.layoutHelpers.getAdjustedQuads(this.currentNode, boxType);
if (!quads) {
continue;
}
let {p1, p2, p3, p4} = quads;
let boxNode = this._boxModelNodes[boxType];

Просмотреть файл

@ -438,12 +438,6 @@ var DebuggerServer = {
return this._listeners.length;
},
// TODO: Bug 1033079: Remove after cleaning up Gaia test:
// https://github.com/mozilla-b2g/gaia/blob/1ba15ce1ae7254badd25fd276556c1b4f36c0a45/tests/integration/devtools/server_test.js#L31
get _listener() {
return this.listeningSockets;
},
/**
* Listens on the given port or socket file for remote debugger connections.
*

Просмотреть файл

@ -134,6 +134,7 @@ OS_LIBS += \
-lstagefright_omx \
-lbinder \
-lgui \
-lmtp \
$(NULL)
endif

Просмотреть файл

@ -290,45 +290,28 @@ function _getBestToFocus(nodes, key, currentlyFocused) {
// Initialize best to the first viable value:
if (!best) {
best = nodes[i];
bestDist = _spatialDistance(best, currentlyFocused);
bestDist = _spatialDistanceOfCorner(currentlyFocused, nodes[i], key);
if (bestDist >= 0) {
best = nodes[i];
}
continue;
}
// Of the remaining nodes, pick the one closest to the currently focused
// node.
let curDist = _spatialDistance(nodes[i], currentlyFocused);
if (curDist > bestDist) {
let curDist = _spatialDistanceOfCorner(currentlyFocused, nodes[i], key);
if ((curDist > bestDist) || curDist === -1) {
continue;
}
bestMid = _getMidpoint(best);
switch (key) {
case PrefObserver['keyCodeLeft']:
if (nodeMid.x > bestMid.x) {
best = nodes[i];
bestDist = curDist;
}
break;
case PrefObserver['keyCodeRight']:
if (nodeMid.x < bestMid.x) {
best = nodes[i];
bestDist = curDist;
}
break;
case PrefObserver['keyCodeUp']:
if (nodeMid.y > bestMid.y) {
best = nodes[i];
bestDist = curDist;
}
break;
case PrefObserver['keyCodeDown']:
if (nodeMid.y < bestMid.y) {
best = nodes[i];
bestDist = curDist;
}
break;
else if (curDist === bestDist) {
let midCurDist = _spatialDistance(currentlyFocused, nodes[i]);
let midBestDist = _spatialDistance(currentlyFocused, best);
if (midCurDist > midBestDist)
continue;
}
best = nodes[i];
bestDist = curDist;
}
return best;
}
@ -376,23 +359,43 @@ function _getSearchRect(currentlyFocused, key, cssPageRect) {
switch (key) {
case PrefObserver['keyCodeLeft']:
newRect.right = newRect.left;
newRect.left = cssPageRect.left;
newRect.width = newRect.right - newRect.left;
break;
case PrefObserver['keyCodeRight']:
newRect.right = cssPageRect.right;
newRect.width = newRect.right - newRect.left;
break;
case PrefObserver['keyCodeUp']:
newRect.bottom = cssPageRect.bottom;
newRect.top = cssPageRect.top;
newRect.height = newRect.bottom - newRect.top;
break;
case PrefObserver['keyCodeRight']:
newRect.left = newRect.right;
newRect.right = cssPageRect.right;
newRect.width = newRect.right - newRect.left;
newRect.bottom = cssPageRect.bottom;
newRect.top = cssPageRect.top;
newRect.height = newRect.bottom - newRect.top;
break;
case PrefObserver['keyCodeUp']:
newRect.bottom = newRect.top;
newRect.top = cssPageRect.top;
newRect.height = newRect.bottom - newRect.top;
newRect.right = cssPageRect.right;
newRect.left = cssPageRect.left;
newRect.width = newRect.right - newRect.left;
break;
case PrefObserver['keyCodeDown']:
newRect.top = newRect.bottom;
newRect.bottom = cssPageRect.bottom;
newRect.height = newRect.bottom - newRect.top;
newRect.right = cssPageRect.right;
newRect.left = cssPageRect.left;
newRect.width = newRect.right - newRect.left;
break;
}
return newRect;
@ -407,6 +410,83 @@ function _spatialDistance(a, b) {
Math.pow(mida.y - midb.y, 2));
}
// Get the distance between the corner of two nodes
function _spatialDistanceOfCorner(from, to, key) {
let fromRect = from.getBoundingClientRect();
let toRect = to.getBoundingClientRect();
let fromMid = _getMidpoint(from);
let toMid = _getMidpoint(to);
let hDistance = 0;
let vDistance = 0;
switch (key) {
case PrefObserver['keyCodeLeft']:
// Make sure the "to" node is really at the left side of "from" node by
// 1. Check the mid point
// 2. The right border of "to" node must be less than the "from" node
if ((fromMid.x - toMid.x) < 0 || toRect.right >= fromRect.right)
return -1;
hDistance = Math.abs(fromRect.left - toRect.right);
if (toRect.bottom <= fromRect.top) {
vDistance = fromRect.top - toRect.bottom;
}
else if (fromRect.bottom <= toRect.top) {
vDistance = toRect.top - fromRect.bottom;
}
else {
vDistance = 0;
}
break;
case PrefObserver['keyCodeRight']:
if ((toMid.x - fromMid.x) < 0 || toRect.left <= fromRect.left)
return -1;
hDistance = Math.abs(toRect.left - fromRect.right);
if (toRect.bottom <= fromRect.top) {
vDistance = fromRect.top - toRect.bottom;
}
else if (fromRect.bottom <= toRect.top) {
vDistance = toRect.top - fromRect.bottom;
}
else {
vDistance = 0;
}
break;
case PrefObserver['keyCodeUp']:
if ((fromMid.y - toMid.y) < 0 || toRect.bottom >= fromRect.bottom)
return -1;
vDistance = Math.abs(fromRect.top - toRect.bottom);
if (fromRect.right <= toRect.left) {
hDistance = toRect.left - fromRect.right;
}
else if (toRect.right <= fromRect.left) {
hDistance = fromRect.left - toRect.right;
}
else {
hDistance = 0;
}
break;
case PrefObserver['keyCodeDown']:
if ((toMid.y - fromMid.y) < 0 || toRect.top <= fromRect.top)
return -1;
vDistance = Math.abs(toRect.top - fromRect.bottom);
if (fromRect.right <= toRect.left) {
hDistance = toRect.left - fromRect.right;
}
else if (toRect.right <= fromRect.left) {
hDistance = fromRect.left - toRect.right;
}
else {
hDistance = 0;
}
break;
}
return Math.round(Math.pow(hDistance, 2) +
Math.pow(vDistance, 2));
}
// Snav preference observer
var PrefObserver = {
register: function() {
@ -524,4 +604,4 @@ var PrefObserver = {
}
};
PrefObserver.register();
PrefObserver.register();

Просмотреть файл

@ -39,22 +39,22 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=698437
" not become the active element?");
synthesizeKey("VK_UP", { });
is(top.id, document.activeElement.id,
is(document.activeElement.id, top.id,
"Spatial navigation up key is not handled correctly.");
center.focus();
synthesizeKey("VK_DOWN", { });
is(bottom.id, document.activeElement.id,
is(document.activeElement.id, bottom.id,
"Spatial navigation down key is not handled correctly.");
center.focus();
synthesizeKey("VK_RIGHT", { });
is(right.id, document.activeElement.id,
is(document.activeElement.id, right.id,
"Spatial navigation right key is not handled correctly.");
center.focus();
synthesizeKey("VK_LEFT", { });
is(left.id, document.activeElement.id,
is(document.activeElement.id, left.id,
"Spatial navigation left key is not handled correctly.");
SimpleTest.finish();
@ -66,11 +66,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=698437
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=698437">Mozilla Bug 698437</a>
<p id="display"></p>
<div id="content">
<p> This is a <a id="top" href="#">really</a> long sentence </p>
<p> <a id="left" href="#">This</a> is a
<a id="center" href="#">really</a> long
<a id="right" href="#">sentence</a> </p>
<p> This is a <a id="bottom" href="#">really</a> long sentence </p>
<button id="lefttop">1</button><button id="top">2</button><button id="righttop">3</button><br>
<button id="left">4</button><button id="center">5</button><button id="right">6</button><br>
<button id="leftbottom">7</button><button id="bottom">8</button><button id="rightbottom">9</button><br>
</div>
<pre id="test">
</pre>