зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound
This commit is contained in:
Коммит
ab953e69d8
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче