Merge mozilla-central to mozilla-inbound on a CLOSED TREE

--HG--
extra : amend_source : 42c86ef1edf2141b6ac81506dd8a31cd68818292
This commit is contained in:
Carsten "Tomcat" Book 2014-03-07 13:55:46 +01:00
Родитель ce84311eeb 885e6e3586
Коммит 0264444cdb
86 изменённых файлов: 1872 добавлений и 255 удалений

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

@ -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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>

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

@ -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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>

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

@ -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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>

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

@ -4,6 +4,6 @@
"branch": "",
"revision": ""
},
"revision": "44e3bac38280fde9b088dfb501d567e0d0e17ccf",
"revision": "1ad040b490a7eca701eab481a3716c5404041c1b",
"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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>

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

@ -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="04eb7996543f114133d1367f834a4d88b68c60ac"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b3758a90b8888e9d95128846b2833b4d9444ef7f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -1120,6 +1120,14 @@ pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","t
pref("devtools.toolbox.sideEnabled", true);
pref("devtools.toolbox.zoomValue", "1");
// Toolbox Button preferences
pref("devtools.command-button-pick.enabled", true);
pref("devtools.command-button-splitconsole.enabled", true);
pref("devtools.command-button-paintflashing.enabled", false);
pref("devtools.command-button-tilt.enabled", false);
pref("devtools.command-button-scratchpad.enabled", false);
pref("devtools.command-button-responsive.enabled", true);
// Inspector preferences
// Enable the Inspector
pref("devtools.inspector.enabled", true);

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

@ -9,3 +9,7 @@
<svg:clipPath id="tab-curve-clip-path-end" clipPathUnits="objectBoundingBox">
<svg:path d="m 0,0.0625 -0.05,0 0,0.938 1,0 0,-0.028 C 0.67917542,0.95840561 0.56569036,0.81970962 0.51599998,0.5625 0.48279998,0.3905 0.465,0.0659 0,0.0625 z"/>
</svg:clipPath>
<svg:clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
<svg:path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
</svg:clipPath>

До

Ширина:  |  Высота:  |  Размер: 731 B

После

Ширина:  |  Высота:  |  Размер: 865 B

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

@ -237,7 +237,8 @@
flip="none"
side="left"
position="leftcenter topright"
noautohide="true">
noautohide="true"
hidden="true">
<hbox class="customization-tipPanel-wrapper">
<vbox class="customization-tipPanel-infoBox"/>
<vbox class="customization-tipPanel-content" flex="1">

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

@ -569,6 +569,7 @@ CustomizeMode.prototype = {
});
}
this.tipPanel.hidden = false;
this.tipPanel.openPopup(anchorNode);
Services.prefs.setBoolPref(kShownPref, true);
},

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

@ -47,11 +47,6 @@
padding: 4px 0 0; /* To align it with the checkbox */
}
.options-citation-label + label {
padding: 3px 0 0 !important; /* To align it with the checkbox */
font-style: italic;
}
.hidden-labels-box:not(.visible) > label,
.hidden-labels-box.visible ~ .hidden-labels-box > label:last-child {
display: none;

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

@ -15,6 +15,7 @@ support-files =
[browser_toolbox_highlight.js]
[browser_toolbox_hosts.js]
[browser_toolbox_options.js]
[browser_toolbox_options_disable_buttons.js]
[browser_toolbox_options_disable_cache.js]
[browser_toolbox_options_disable_js.js]
# [browser_toolbox_raise.js] # Bug 962258

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

@ -0,0 +1,148 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
let doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
let target = TargetFactory.forTab(gBrowser.selectedTab);
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
gDevTools.showToolbox(target)
.then(testSelectTool)
.then(testToggleToolboxButtons)
.then(testPrefsAreRespectedWhenReopeningToolbox)
.then(cleanup, errorHandler);
}, true);
content.location = "data:text/html;charset=utf8,test for dynamically registering and unregistering tools";
}
function testPrefsAreRespectedWhenReopeningToolbox() {
let deferred = promise.defer();
let target = TargetFactory.forTab(gBrowser.selectedTab);
info ("Closing toolbox to test after reopening");
gDevTools.closeToolbox(target).then(() => {
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target)
.then(testSelectTool)
.then(() => {
info ("Toolbox has been reopened. Checking UI state.");
testPreferenceAndUIStateIsConsistent();
deferred.resolve();
});
});
return deferred.promise;
}
function testSelectTool(aToolbox) {
let deferred = promise.defer();
info ("Selecting the options panel");
toolbox = aToolbox;
doc = toolbox.doc;
toolbox.once("options-selected", (event, tool) => {
ok(true, "Options panel selected via selectTool method");
panelWin = tool.panelWin;
deferred.resolve();
});
toolbox.selectTool("options");
return deferred.promise;
}
function testPreferenceAndUIStateIsConsistent() {
let checkNodes = [...panelWin.document.querySelectorAll("#enabled-toolbox-buttons-box > checkbox")];
let toolboxButtonNodes = [...doc.querySelectorAll("#toolbox-buttons > toolbarbutton")];
let toggleableTools = toolbox.toolboxButtons;
for (let tool of toggleableTools) {
let isVisible = getBoolPref(tool.visibilityswitch);
let button = toolboxButtonNodes.filter(button=>button.id === tool.id)[0];
is (!button.hasAttribute("hidden"), isVisible, "Button visibility matches pref for " + tool.id);
let check = checkNodes.filter(node=>node.id === tool.id)[0];
is (check.checked, isVisible, "Checkbox should be selected based on current pref for " + tool.id);
}
}
function testToggleToolboxButtons() {
let checkNodes = [...panelWin.document.querySelectorAll("#enabled-toolbox-buttons-box > checkbox")];
let toolboxButtonNodes = [...doc.querySelectorAll("#toolbox-buttons > toolbarbutton")];
let visibleButtons = toolboxButtonNodes.filter(button=>!button.hasAttribute("hidden"));
let toggleableTools = toolbox.toolboxButtons;
is (checkNodes.length, toggleableTools.length, "All of the buttons are toggleable." );
is (checkNodes.length, toolboxButtonNodes.length, "All of the DOM buttons are toggleable." );
for (let tool of toggleableTools) {
let id = tool.id;
let matchedCheckboxes = checkNodes.filter(node=>node.id === id);
let matchedButtons = toolboxButtonNodes.filter(button=>button.id === id);
ok (matchedCheckboxes.length === 1,
"There should be a single toggle checkbox for: " + id);
ok (matchedButtons.length === 1,
"There should be a DOM button for: " + id);
is (matchedButtons[0], tool.button,
"DOM buttons should match for: " + id);
is (matchedCheckboxes[0].getAttribute("label"), tool.label,
"The label for checkbox matches the tool definition.")
is (matchedButtons[0].getAttribute("tooltiptext"), tool.label,
"The tooltip for button matches the tool definition.")
}
// Store modified pref names so that they can be cleared on error.
for (let tool of toggleableTools) {
let pref = tool.visibilityswitch;
modifiedPrefs.push(pref);
}
// Try checking each checkbox, making sure that it changes the preference
for (let node of checkNodes) {
let tool = toggleableTools.filter(tool=>tool.id === node.id)[0];
let isVisible = getBoolPref(tool.visibilityswitch);
testPreferenceAndUIStateIsConsistent();
toggleButton(node);
testPreferenceAndUIStateIsConsistent();
let isVisibleAfterClick = getBoolPref(tool.visibilityswitch);
is (isVisible, !isVisibleAfterClick,
"Clicking on the node should have toggled visibility preference for " + tool.visibilityswitch);
}
return promise.resolve();
}
function getBoolPref(key) {
return Services.prefs.getBoolPref(key);
}
function toggleButton(node) {
node.scrollIntoView();
EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
}
function cleanup() {
toolbox.destroy().then(function() {
gBrowser.removeCurrentTab();
for (let pref of modifiedPrefs) {
Services.prefs.clearUserPref(pref);
}
toolbox = doc = panelWin = modifiedPrefs = null;
finish();
});
}
function errorHandler(error) {
ok(false, "Unexpected error: " + error);
cleanup();
}

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

@ -61,6 +61,7 @@ OptionsPanel.prototype = {
return targetPromise.then(() => {
this.setupToolsList();
this.setupToolbarButtonsList();
this.populatePreferences();
this._disableJSClicked = this._disableJSClicked.bind(this);
@ -81,6 +82,43 @@ OptionsPanel.prototype = {
});
},
setupToolbarButtonsList: function() {
let enabledToolbarButtonsBox = this.panelDoc.getElementById("enabled-toolbox-buttons-box");
enabledToolbarButtonsBox.textContent = "";
let toggleableButtons = this.toolbox.toolboxButtons;
let setToolboxButtonsVisibility =
this.toolbox.setToolboxButtonsVisibility.bind(this.toolbox);
let onCheckboxClick = (checkbox) => {
let toolDefinition = toggleableButtons.filter(tool => tool.id === checkbox.id)[0];
Services.prefs.setBoolPref(toolDefinition.visibilityswitch, checkbox.checked);
setToolboxButtonsVisibility();
};
let createCommandCheckbox = tool => {
let checkbox = this.panelDoc.createElement("checkbox");
checkbox.setAttribute("id", tool.id);
checkbox.setAttribute("label", tool.label);
checkbox.setAttribute("checked", this.getBoolPref(tool.visibilityswitch));
checkbox.addEventListener("command", onCheckboxClick.bind(this, checkbox));
return checkbox;
};
for (let tool of toggleableButtons) {
enabledToolbarButtonsBox.appendChild(createCommandCheckbox(tool));
}
},
getBoolPref: function(key) {
try {
return Services.prefs.getBoolPref(key);
}
catch (ex) {
return true;
}
},
setupToolsList: function() {
let defaultToolsBox = this.panelDoc.getElementById("default-tools-box");
let additionalToolsBox = this.panelDoc.getElementById("additional-tools-box");
@ -90,15 +128,6 @@ OptionsPanel.prototype = {
defaultToolsBox.textContent = "";
additionalToolsBox.textContent = "";
let pref = function(key) {
try {
return Services.prefs.getBoolPref(key);
}
catch (ex) {
return true;
}
};
let onCheckboxClick = function(id) {
let toolDefinition = gDevTools._tools.get(id);
// Set the kill switch pref boolean to true
@ -124,7 +153,7 @@ OptionsPanel.prototype = {
l10n("options.toolNotSupportedMarker", tool.label));
checkbox.setAttribute("unsupported", "");
}
checkbox.setAttribute("checked", pref(tool.visibilityswitch));
checkbox.setAttribute("checked", this.getBoolPref(tool.visibilityswitch));
checkbox.addEventListener("command", onCheckboxClick.bind(checkbox, tool.id));
return checkbox;
};

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

@ -20,9 +20,12 @@
<vbox id="default-tools-box" class="options-groupbox" tabindex="0"/>
<label value="&options.selectAdditionalTools.label;"/>
<vbox id="additional-tools-box" class="options-groupbox"/>
<label value="&options.selectEnabledToolboxButtons.label;"/>
<vbox id="enabled-toolbox-buttons-box" class="options-groupbox"/>
<label id="tools-not-supported-label"
class="options-citation-label theme-comment"
value="&options.toolNotSupported.label;"/>
</vbox>
<vbox class="options-vertical-pane" flex="1">
<label value="&options.selectDevToolsTheme.label;"/>

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

@ -543,6 +543,7 @@ Toolbox.prototype = {
this.doc, this._requisition);
let container = this.doc.getElementById("toolbox-buttons");
buttons.forEach(container.appendChild.bind(container));
this.setToolboxButtonsVisibility();
},
/**
@ -562,6 +563,57 @@ Toolbox.prototype = {
this._pickerButton.addEventListener("command", this._togglePicker, false);
},
/**
* Return all toolbox buttons (command buttons, plus any others that were
* added manually).
*/
get toolboxButtons() {
// White-list buttons that can be toggled to prevent adding prefs for
// addons that have manually inserted toolbarbuttons into DOM.
return [
"command-button-pick",
"command-button-splitconsole",
"command-button-responsive",
"command-button-paintflashing",
"command-button-tilt",
"command-button-scratchpad"
].map(id => {
let button = this.doc.getElementById(id);
// Some buttons may not exist inside of Browser Toolbox
if (!button) {
return false;
}
return {
id: id,
button: button,
label: button.getAttribute("tooltiptext"),
visibilityswitch: "devtools." + id + ".enabled"
}
}).filter(button=>button);
},
/**
* Ensure the visibility of each toolbox button matches the
* preference value. Simply hide buttons that are preffed off.
*/
setToolboxButtonsVisibility: function() {
this.toolboxButtons.forEach(buttonSpec => {
let {visibilityswitch, id, button}=buttonSpec;
let on = true;
try {
on = Services.prefs.getBoolPref(visibilityswitch);
} catch (ex) { }
if (button) {
if (on) {
button.removeAttribute("hidden");
} else {
button.setAttribute("hidden", "true");
}
}
});
},
/**
* Build a tab for one tool definition and add to the toolbox
*

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

@ -92,6 +92,11 @@
- installed by add-ons. -->
<!ENTITY options.selectAdditionalTools.label "Developer Tools installed by add-ons">
<!-- LOCALIZATION NOTE (options.selectEnabledToolboxButtons.label): This is the label for
- the heading of group of checkboxes corresponding to the default developer
- tool buttons. -->
<!ENTITY options.selectEnabledToolboxButtons.label "Available Toolbox Buttons">
<!-- LOCALIZATION NOTE (options.toolNotSupported.label): This is the label for
- the explanation of the * marker on a tool which is currently not supported
- for the target of the toolbox. -->

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

@ -27,6 +27,11 @@ function tearDown() {
PanelUI.hide();
BookmarksTestHelper.restore();
HistoryTestHelper.restore();
Browser.selectedTab
.browser
.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).clearNativeTouchSequence();
}
/*
@ -85,7 +90,7 @@ gTests.push({
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
var touchdrag = new TouchDragAndHold();
touchdrag.useNativeEvents = true;
touchdrag.stepTimeout = 20;
touchdrag.stepTimeout = 5;
touchdrag.numSteps = 20;
stopwatch.start();
@ -122,7 +127,7 @@ gTests.push({
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
var touchdrag = new TouchDragAndHold();
touchdrag.useNativeEvents = true;
touchdrag.stepTimeout = 20;
touchdrag.stepTimeout = 5;
touchdrag.numSteps = 10;
let iterations = 3;

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

@ -201,13 +201,10 @@ menulist {
-moz-box-align: center;
font-weight: 600;
}
.menu-popup > richlistbox > richlistitem {
padding-right: 50px;
-moz-padding-end: 50px;
}
/* Additional styles applied to popups for form <select> elements. */
#select-container {
padding: 0;
position: absolute;

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

@ -127,17 +127,17 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
/* ----- BOOKMARK STAR ANIMATION ----- */
@keyframes animation-bookmarkAdded {
from { transform: rotate(0deg) translateX(-20px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-20px) rotate(-180deg) scale(2.2); opacity: 1; }
from { transform: rotate(0deg) translateX(-16px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-16px) rotate(-180deg) scale(2.2); opacity: 1; }
80% { opacity: 1; }
to { transform: rotate(180deg) translateX(-20px) rotate(-180deg) scale(1); opacity: 0; }
to { transform: rotate(180deg) translateX(-16px) rotate(-180deg) scale(1); opacity: 0; }
}
@keyframes animation-bookmarkAddedToBookmarksBar {
from { transform: rotate(0deg) translateX(-12px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-12px) rotate(-180deg) scale(2.2); opacity: 1; }
from { transform: rotate(0deg) translateX(-10px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-10px) rotate(-180deg) scale(2.2); opacity: 1; }
80% { opacity: 1; }
to { transform: rotate(180deg) translateX(-12px) rotate(-180deg) scale(1); opacity: 0; }
to { transform: rotate(180deg) translateX(-10px) rotate(-180deg) scale(1); opacity: 0; }
}
@keyframes animation-bookmarkPulse {
@ -174,6 +174,10 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
animation: animation-bookmarkAddedToBookmarksBar 800ms;
}
#bookmarks-menu-button[notification="finish"] {
pointer-events: none;
}
#bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
animation: animation-bookmarkPulse 300ms;
animation-delay: 600ms;
@ -573,7 +577,7 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
border: 1px solid transparent;
border-radius: 2px;
transition-property: background-color, border-color;
transition-duration: 250ms;
transition-duration: 150ms;
}
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
@ -596,8 +600,8 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
-moz-padding-end: 5px;
}
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover > .toolbarbutton-icon,
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([buttonover]):not([open]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-icon {
background-color: hsla(0,0%,100%,.3);
background-image: linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.2));
@ -648,6 +652,10 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
box-shadow: 0 0 0 1px hsla(0,0%,100%,.2);
}
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-moz-margin-start: -4px;
}
#forward-button[disabled] {
transform: scale(0);
opacity: 0;

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

@ -73,6 +73,7 @@ browser.jar:
skin/classic/browser/webRTC-sharingMicrophone-16.png
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
skin/classic/browser/customizableui/customize-illustration.png (../shared/customizableui/customize-illustration.png)
skin/classic/browser/customizableui/customize-illustration-rtl.png (../shared/customizableui/customize-illustration-rtl.png)
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)

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

@ -414,6 +414,10 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
}
}
#bookmarks-menu-button[notification="finish"] {
pointer-events: none;
}
#bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
animation: animation-bookmarkPulse 300ms;
animation-delay: 600ms;
@ -4261,6 +4265,10 @@ window > chatbox {
list-style-image: url(chrome://browser/skin/customizableui/customize-illustration@2x.png);
}
.customization-tipPanel-contentImage:-moz-locale-dir(rtl) {
list-style-image: url(chrome://browser/skin/customizableui/customize-illustration-rtl@2x.png);
}
#customization-tipPanel > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow[side="left"],
#customization-tipPanel > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow[side="right"] {
list-style-image: url("chrome://browser/skin/customizableui/panelarrow-customizeTip@2x.png");

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

@ -5,8 +5,8 @@
/*** Status and progress indicator ***/
#downloads-indicator-anchor {
min-width: 20px;
min-height: 20px;
min-width: 18px;
min-height: 18px;
}
#downloads-animation-container {

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

@ -124,6 +124,8 @@ browser.jar:
skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png (customizableui/customize-titleBar-toggle@2x.png)
skin/classic/browser/customizableui/customize-illustration.png (../shared/customizableui/customize-illustration.png)
skin/classic/browser/customizableui/customize-illustration@2x.png (../shared/customizableui/customize-illustration@2x.png)
skin/classic/browser/customizableui/customize-illustration-rtl.png (../shared/customizableui/customize-illustration-rtl.png)
skin/classic/browser/customizableui/customize-illustration-rtl@2x.png (../shared/customizableui/customize-illustration-rtl@2x.png)
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.4 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 17 KiB

После

Ширина:  |  Высота:  |  Размер: 7.4 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 41 KiB

После

Ширина:  |  Высота:  |  Размер: 16 KiB

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

@ -74,7 +74,7 @@
outline-offset: -5px;
}
#main-window[customize-entered] .customization-target:not(#PanelUI-contents) {
#main-window[customizing] .customization-target:not(#PanelUI-contents) {
min-width: 100px;
padding-left: 10px;
padding-right: 10px;

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

@ -46,6 +46,10 @@
display: -moz-box;
}
.customization-tipPanel-contentImage:-moz-locale-dir(rtl) {
list-style-image: url(chrome://browser/skin/customizableui/customize-illustration-rtl.png);
}
.customization-tipPanel-link {
-moz-appearance: none;
background: transparent;

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

@ -519,6 +519,11 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
color: white;
background-color: rgb(116,191,67);
text-shadow: none;
margin-top: -1px;
}
#customization-panelHolder #PanelUI-customize + toolbarseparator {
display: none;
}
#customization-panelHolder #PanelUI-customize:hover,

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

@ -227,6 +227,20 @@
/* End selected tab */
/* Background tabs */
/* Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
the titlebar. We don't need this in fullscreen since window dragging is not an issue there. */
%ifdef XP_MACOSX
#main-window[tabsintitlebar][sizemode="maximized"] .tab-background-middle:not([selected=true]),
%endif
#main-window[tabsintitlebar]:not([sizemode="maximized"]):not([inFullscreen]) #toolbar-menubar:-moz-any([autohide="true"][inactive], :not([autohide])) + #TabsToolbar .tab-background-middle:not([selected=true]) {
clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
}
/* End background tabs */
/* new tab button border and gradient on hover */
.tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]),
.tabs-newtab-button:hover {

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

@ -364,6 +364,10 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
animation-timing-function: ease, ease, ease;
}
#bookmarks-menu-button[notification="finish"] {
pointer-events: none;
}
#bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
animation: animation-bookmarkPulse 300ms;
animation-delay: 600ms;

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

@ -92,6 +92,7 @@ browser.jar:
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
skin/classic/browser/customizableui/customize-illustration.png (../shared/customizableui/customize-illustration.png)
skin/classic/browser/customizableui/customize-illustration-rtl.png (../shared/customizableui/customize-illustration-rtl.png)
skin/classic/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
@ -417,6 +418,7 @@ browser.jar:
skin/classic/aero/browser/webRTC-sharingMicrophone-16.png
skin/classic/aero/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
skin/classic/aero/browser/customizableui/customize-illustration.png (../shared/customizableui/customize-illustration.png)
skin/classic/aero/browser/customizableui/customize-illustration-rtl.png (../shared/customizableui/customize-illustration-rtl.png)
skin/classic/aero/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
skin/classic/aero/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
skin/classic/aero/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)

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

@ -134,6 +134,9 @@ CameraCapabilities::Populate(ICameraControl* aCameraControl)
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES, mFocusModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ISOMODES, mIsoModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS);
@ -278,5 +281,11 @@ CameraCapabilities::RecorderProfiles(JSContext* aCx) const
return mRecorderProfiles;
}
void
CameraCapabilities::GetIsoModes(nsTArray<nsString>& retval) const
{
retval = mIsoModes;
}
} // namespace dom
} // namespace mozilla

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

@ -59,6 +59,7 @@ public:
double MaxExposureCompensation() const;
double ExposureCompensationStep() const;
JS::Value RecorderProfiles(JSContext* cx) const;
void GetIsoModes(nsTArray<nsString>& aRetVal) const;
protected:
nsresult TranslateToDictionary(ICameraControl* aCameraControl,
@ -75,6 +76,7 @@ protected:
nsTArray<nsString> mEffects;
nsTArray<nsString> mFlashModes;
nsTArray<nsString> mFocusModes;
nsTArray<nsString> mIsoModes;
nsTArray<double> mZoomRatios;

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

@ -11,6 +11,7 @@
#include "DeviceStorage.h"
#include "DeviceStorageFileDescriptor.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/MediaManager.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
@ -32,6 +33,7 @@
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl)
NS_INTERFACE_MAP_ENTRY(nsISupports)
@ -395,6 +397,19 @@ nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode, ErrorResult& aRv)
aRv = mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode);
}
void
nsDOMCameraControl::GetIsoMode(nsString& aIsoMode, ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
aRv = mCameraControl->Get(CAMERA_PARAM_ISOMODE, aIsoMode);
}
void
nsDOMCameraControl::SetIsoMode(const nsAString& aIsoMode, ErrorResult& aRv)
{
MOZ_ASSERT(mCameraControl);
aRv = mCameraControl->Set(CAMERA_PARAM_ISOMODE, aIsoMode);
}
double
nsDOMCameraControl::GetZoom(ErrorResult& aRv)
{
@ -726,8 +741,16 @@ nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
return;
}
}
OnError(CameraControlListener::kInStartRecording, NS_LITERAL_STRING("FAILURE"));
if (mDSFileDescriptor->mFileDescriptor.IsValid()) {
// An error occured. We need to manually close the file associated with the
// FileDescriptor, and we shouldn't do this on the main thread, so we
// use a little helper.
nsRefPtr<CloseFileRunnable> closer =
new CloseFileRunnable(mDSFileDescriptor->mFileDescriptor);
closer->Dispatch();
}
}
void

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

@ -79,6 +79,8 @@ public:
double GetExposureCompensation(ErrorResult& aRv);
int32_t SensorAngle();
already_AddRefed<dom::CameraCapabilities> Capabilities();
void GetIsoMode(nsString& aMode, ErrorResult& aRv);
void SetIsoMode(const nsAString& aMode, ErrorResult& aRv);
// Unsolicited event handlers.
already_AddRefed<dom::CameraShutterCallback> GetOnShutter();

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

@ -30,6 +30,7 @@
#include "mozilla/FileUtils.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "nsAlgorithm.h"
#include <media/mediaplayer.h>
#include "nsPrintfCString.h"
@ -46,6 +47,7 @@
using namespace mozilla;
using namespace mozilla::layers;
using namespace mozilla::gfx;
using namespace mozilla::ipc;
using namespace android;
#define RETURN_IF_NO_CAMERA_HW() \
@ -855,6 +857,14 @@ nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescri
return NS_ERROR_INVALID_ARG;
}
// SetupRecording creates a dup of the file descriptor, so we need to
// close the file descriptor when we leave this function. Also note, that
// since we're already off the main thread, we don't need to dispatch this.
// We just let the CloseFileRunnable destructor do the work.
nsRefPtr<CloseFileRunnable> closer;
if (aFileDescriptor->mFileDescriptor.IsValid()) {
closer = new CloseFileRunnable(aFileDescriptor->mFileDescriptor);
}
nsresult rv;
int fd = aFileDescriptor->mFileDescriptor.PlatformHandle();
if (aOptions) {

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

@ -77,6 +77,10 @@ GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
return "exif-datetime";
case CAMERA_PARAM_VIDEOSIZE:
return KEY_VIDEO_SIZE;
case CAMERA_PARAM_ISOMODE:
// Not every platform defines KEY_ISO_MODE;
// for those that don't, we use the raw string key.
return "iso";
case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
return KEY_SUPPORTED_PREVIEW_SIZES;
@ -112,6 +116,10 @@ GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
return KEY_ZOOM_RATIOS;
case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES:
return KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
case CAMERA_PARAM_SUPPORTED_ISOMODES:
// Not every platform defines KEY_SUPPORTED_ISO_MODES;
// for those that don't, we use the raw string key.
return "iso-values";
default:
DOM_CAMERA_LOGE("Unhandled camera parameter value %u\n", aKey);
return nullptr;
@ -138,6 +146,43 @@ GonkCameraParameters::~GonkCameraParameters()
}
}
nsresult
GonkCameraParameters::MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut)
{
if (aIso.EqualsASCII("hjr")) {
aIsoOut = "ISO_HJR";
} else if (aIso.EqualsASCII("auto")) {
aIsoOut = "auto";
} else {
nsAutoCString v = NS_LossyConvertUTF16toASCII(aIso);
unsigned int iso;
if (sscanf(v.get(), "%u", &iso) != 1) {
return NS_ERROR_FAILURE;
}
aIsoOut = nsPrintfCString("ISO%u", iso);
}
return NS_OK;
}
nsresult
GonkCameraParameters::MapIsoFromGonk(const char* aIso, nsAString& aIsoOut)
{
if (strcmp(aIso, "ISO_HJR") == 0) {
aIsoOut.AssignASCII("hjr");
} else if (strcmp(aIso, "auto") == 0) {
aIsoOut.AssignASCII("auto");
} else {
unsigned int iso;
if (sscanf(aIso, "ISO%u", &iso) != 1) {
return NS_ERROR_FAILURE;
}
aIsoOut.AppendInt(iso);
}
return NS_OK;
}
// Any members that need to be initialized on the first parameter pull
// need to get handled in here.
nsresult
@ -155,11 +200,39 @@ GonkCameraParameters::Initialize()
NS_WARNING("Failed to initialize exposure compensation step size");
mExposureCompensationStep = 0;
}
rv = GetListAsArray(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
if (NS_FAILED(rv)) {
// zoom is not supported
mZoomRatios.Clear();
}
for (uint32_t i = 1; i < mZoomRatios.Length(); ++i) {
// Make sure the camera gave us a properly sorted zoom ratio list!
if (mZoomRatios[i] < mZoomRatios[i - 1]) {
NS_WARNING("Zoom ratios list is out of order, discarding");
DOM_CAMERA_LOGE("zoom[%d]=%fx < zoom[%d]=%fx is out of order\n",
i, mZoomRatios[i] / 100.0, i - 1, mZoomRatios[i - 1] / 100.0);
mZoomRatios.Clear();
break;
}
}
if (mZoomRatios.Length() == 0) {
// Always report that we support at least 1.0x zoom.
*mZoomRatios.AppendElement() = 100;
}
// The return code from GetListAsArray() doesn't matter. If it fails,
// the isoModes array will be empty, and the subsequent loop won't
// execute.
nsTArray<nsCString> isoModes;
GetListAsArray(CAMERA_PARAM_SUPPORTED_ISOMODES, isoModes);
for (uint32_t i = 0; i < isoModes.Length(); ++i) {
nsString v;
rv = MapIsoFromGonk(isoModes[i].get(), v);
if (NS_SUCCEEDED(rv)) {
*mIsoModes.AppendElement() = v;
}
}
mInitialized = true;
return NS_OK;
@ -169,6 +242,15 @@ GonkCameraParameters::Initialize()
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue)
{
if (aKey == CAMERA_PARAM_ISOMODE) {
nsAutoCString v;
nsresult rv = MapIsoToGonk(aValue, v);
if (NS_FAILED(rv)) {
return rv;
}
return SetImpl(aKey, v.get());
}
return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
}
@ -180,8 +262,12 @@ GonkCameraParameters::GetTranslated(uint32_t aKey, nsAString& aValue)
if (NS_FAILED(rv)) {
return rv;
}
aValue.AssignASCII(val);
return NS_OK;
if (aKey == CAMERA_PARAM_ISOMODE) {
rv = MapIsoFromGonk(val, aValue);
} else {
aValue.AssignASCII(val);
}
return rv;
}
// Handle ICameraControl::Sizes
@ -356,32 +442,47 @@ GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Positio
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const int64_t& aValue)
{
if (aKey == CAMERA_PARAM_PICTURE_DATETIME) {
// Add the non-GPS timestamp. The EXIF date/time field is formatted as
// "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
// is meant to be stored as a local time. Since we are given seconds from
// Epoch GMT, we use localtime_r() to handle the conversion.
time_t time = aValue;
if (time != aValue) {
DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aValue);
return NS_ERROR_INVALID_ARG;
}
switch (aKey) {
case CAMERA_PARAM_PICTURE_DATETIME:
{
// Add the non-GPS timestamp. The EXIF date/time field is formatted as
// "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
// is meant to be stored as a local time. Since we are given seconds from
// Epoch GMT, we use localtime_r() to handle the conversion.
time_t time = aValue;
if (time != aValue) {
DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aValue);
return NS_ERROR_INVALID_ARG;
}
struct tm t;
if (!localtime_r(&time, &t)) {
DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno));
return NS_ERROR_FAILURE;
}
struct tm t;
if (!localtime_r(&time, &t)) {
DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno));
return NS_ERROR_FAILURE;
}
char dateTime[20];
if (!strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
return NS_ERROR_FAILURE;
}
char dateTime[20];
if (!strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
return NS_ERROR_FAILURE;
}
DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
return SetImpl(CAMERA_PARAM_PICTURE_DATETIME, dateTime);
return SetImpl(CAMERA_PARAM_PICTURE_DATETIME, dateTime);
}
case CAMERA_PARAM_ISOMODE:
{
if (aValue > INT32_MAX) {
DOM_CAMERA_LOGW("Can't set ISO mode = %lld, too big\n", aValue);
return NS_ERROR_INVALID_ARG;
}
nsString s;
s.AppendInt(aValue);
return SetTranslated(CAMERA_PARAM_ISOMODE, s);
}
}
// You can't actually pass 64-bit parameters to Gonk. :(
@ -430,20 +531,15 @@ GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue)
case CAMERA_PARAM_ZOOM:
{
if (mZoomRatios.Length() == 0) {
DOM_CAMERA_LOGE("Zoom not supported, can't set %fx\n", aValue);
return NS_ERROR_NOT_AVAILABLE;
}
/**
* Convert from a real zoom multipler (e.g. 2.5x) to
* the index of the nearest supported value.
*/
value = aValue * 100.0;
if (value < mZoomRatios[0]) {
if (value <= mZoomRatios[0]) {
index = 0;
} else if (value > mZoomRatios.LastElement()) {
} else if (value >= mZoomRatios.LastElement()) {
index = mZoomRatios.Length() - 1;
} else {
// mZoomRatios is sorted, so we can binary search it
@ -488,12 +584,13 @@ GonkCameraParameters::GetTranslated(uint32_t aKey, double& aValue)
switch (aKey) {
case CAMERA_PARAM_ZOOM:
rv = GetImpl(CAMERA_PARAM_ZOOM, index);
if (NS_SUCCEEDED(rv)) {
rv = GetImpl(aKey, index);
if (NS_SUCCEEDED(rv) && index >= 0) {
val = mZoomRatios[index] / 100.0;
} else {
// return 1x when zooming is not supported
val = 1.0;
rv = NS_OK;
}
break;
@ -608,6 +705,17 @@ ParseItem(const char* aStart, const char* aEnd, nsAString* aItem)
return NS_OK;
}
nsresult
ParseItem(const char* aStart, const char* aEnd, nsACString* aItem)
{
if (aEnd) {
aItem->AssignASCII(aStart, aEnd - aStart);
} else {
aItem->AssignASCII(aStart);
}
return NS_OK;
}
nsresult
ParseItem(const char* aStart, const char* aEnd, double* aItem)
{
@ -677,6 +785,11 @@ GonkCameraParameters::GetListAsArray(uint32_t aKey, nsTArray<T>& aArray)
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues)
{
if (aKey == CAMERA_PARAM_SUPPORTED_ISOMODES) {
aValues = mIsoModes;
return NS_OK;
}
return GetListAsArray(aKey, aValues);
}
@ -685,7 +798,7 @@ GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<double>& aValues)
{
if (aKey == CAMERA_PARAM_SUPPORTED_ZOOMRATIOS) {
aValues.Clear();
for (int i = 0; i < mZoomRatios.Length(); ++i) {
for (uint32_t i = 0; i < mZoomRatios.Length(); ++i) {
*aValues.AppendElement() = mZoomRatios[i] / 100.0;
}
return NS_OK;

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

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Mozilla Foundation
* Copyright (C) 2013-2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -95,6 +95,7 @@ protected:
double mExposureCompensationMin;
double mExposureCompensationStep;
nsTArray<int> mZoomRatios;
nsTArray<nsString> mIsoModes;
// This subclass of android::CameraParameters just gives
// all of the AOSP getters and setters the same signature.
@ -176,6 +177,8 @@ protected:
nsresult GetTranslated(uint32_t aKey, nsTArray<double>& aValues);
template<class T> nsresult GetListAsArray(uint32_t aKey, nsTArray<T>& aArray);
nsresult MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut);
nsresult MapIsoFromGonk(const char* aIso, nsAString& aIsoOut);
nsresult Initialize();
};

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

@ -50,6 +50,7 @@ enum {
CAMERA_PARAM_THUMBNAILSIZE,
CAMERA_PARAM_THUMBNAILQUALITY,
CAMERA_PARAM_SENSORANGLE,
CAMERA_PARAM_ISOMODE,
// supported features
CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
@ -68,7 +69,8 @@ enum {
CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
CAMERA_PARAM_SUPPORTED_ZOOM,
CAMERA_PARAM_SUPPORTED_ZOOMRATIOS,
CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES
CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES,
CAMERA_PARAM_SUPPORTED_ISOMODES
};
class ICameraControl

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

@ -34,7 +34,7 @@ function onError(e) {
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
'recorderProfiles', 'zoomRatios'];
'recorderProfiles', 'zoomRatios', 'isoModes'];
var Camera = {
cameraObj: null,
@ -151,30 +151,17 @@ var Camera = {
ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
info("zoom ratios length = " + Camera._zoomRatios.length);
ok(camcap.isoModes.length == 0, "ISO modes length = " + camcap.isoModes.length);
if (Camera._zoomRatios.length > 0) {
Camera._zoomRatios.forEach(function(element, index) {
info("zoom[" + index + "] = " + element + "x");
Camera.setZoom(element);
ok(Camera.getZoom() === element, "zoom[" + index + "] = " + element + "x");
});
var zoom = Camera._zoomRatios[0] - 0.1;
Camera.setZoom(zoom);
ok(Camera.getZoom() === Camera._zoomRatios[0],
zoom + "x zoom clamps to minimum: " + Camera._zoomRatios[0]);
zoom = Camera._zoomRatios.slice(-1)[0] + 1.0;
Camera.setZoom(zoom);
ok(Camera.getZoom() === Camera._zoomRatios[0],
zoom + "x zoom clamps to maximum: " + Camera._zoomRatios.slice(-1)[0]);
if (Camera._zoomRatios.length > 1) {
zoom = (Camera._zoomRatios[0] + Camera._zoomRatios[1]) / 2;
Camera.setZoom(zoom);
ok(Camera.getZoom() === Camera._zoomRatios[0],
zoom + "x zoom rounded down to maximum: " + Camera._zoomRatios.slice[0]);
}
}
// The emulator doesn't support zoom, so these parameters will be very constrained
// For more ambitious tests, see test_camera_fake_parameters.html
ok(Camera._zoomRatios.length == 1, "zoom ratios length = " + Camera._zoomRatios.length);
ok(Camera.cameraObj.zoom == 1.0, "zoom = " + Camera.cameraObj.zoom);
// Test snapping to supported values
Camera.cameraObj.zoom = 0.9;
ok(Camera.cameraObj.zoom == 1.0, "zoom = " + Camera.cameraObj.zoom);
Camera.cameraObj.zoom = 1.1;
ok(Camera.cameraObj.zoom == 1.0, "zoom = " + Camera.cameraObj.zoom);
Camera._tests = new Array();
for (var i in Camera._pictureSizes) {

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

@ -27,21 +27,34 @@ var initialConfig = {
var cameraObj = null;
// Shorthand functions
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
function end() {
CameraTest.end();
}
function next() {
CameraTest.next();
if (cameraObj) {
cameraObj.release(
function success() {
CameraTest.next();
},
onError
);
cameraObj = null;
} else {
CameraTest.next();
}
}
function run() {
CameraTest.run();
}
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
}
// The array of tests
// The array of tests. Because camera capabilities don't change over the life
// of a CameraControl object, they are only read once when the CameraControl is
// created; so we have to call the 'prep' function first to set the fake
// capability pref, before we call getCamera() and call 'test' to run the test.
var tests = [
{
key: "fake-zoom",
@ -83,6 +96,49 @@ var tests = [
"x, cam.zoom = " + cam.zoom + "x");
}
next();
}
},
{
key: "fake-zoom-out-of-order",
prep: function setupFakeZoomOutOfOrder(test) {
// We expect the camera library to give us zoom ratios in order; if
// it doesn't we ignore the list and just return 1x support.
test.setFakeParameters("zoom-ratios=100,150,200,400,300;max-zoom=4", function () {
run();
});
},
test: function testFakeZoomOutOfOrder(cam, cap) {
ok(cap.zoomRatios.length == 1, "zoom ratios length = " + cap.zoomRatios.length);
ok(cap.zoomRatios[0] == 1.0, "only supported zoom = " + cap.zoomRatios[0] + "x");
next();
}
},
{
key: "fake-iso",
prep: function setupFakeIso(test) {
// we should recognize 'auto', 'hjr', and numeric modes; anything else
// from the driver is ignored, which this test also verifies.
test.setFakeParameters(
"iso=auto;iso-values=auto,ISO_HJR,ISO100,foo,ISObar,ISO200,ISO400,ISO800,ISO1600",
function () {
run();
});
},
test: function testFakeIso(cam, cap) {
// values 'foo' and 'ISObar' should not be included in isoModes
ok(cap.isoModes.length == 7, "ISO modes length = " + cap.isoModes.length);
ok(cap.isoModes.indexOf("foo") == -1, "Unknown ISO mode 'foo' is ignored");
ok(cap.isoModes.indexOf("ISObar") == -1, "Unknown ISO mode 'ISObar' is ignored");
ok(cap.isoModes.indexOf("bar") == -1, "Unknown ISO mode 'bar' is ignored");
// test individual ISO modes
cap.isoModes.forEach(function(iso, index) {
cam.iso = iso;
ok(cam.iso === iso,
"ISO[" + index + "] = " + iso + ", cam.iso = " + cam.iso);
});
next();
}
},
@ -96,8 +152,10 @@ var testGenerator = function() {
window.addEventListener('beforeunload', function() {
document.getElementById('viewfinder').mozSrcObject = null;
cameraObj.release();
cameraObj = null;
if (cameraObj) {
cameraObj.release();
cameraObj = null;
}
});
CameraTest.begin("hardware", function(test) {
@ -112,7 +170,15 @@ CameraTest.begin("hardware", function(test) {
info("test: " + t.key);
function onSuccess(camera, config) {
cameraObj = camera;
t.test(camera, camera.capabilities);
document.getElementById('viewfinder').mozSrcObject = camera;
camera.onPreviewStateChange = function (state) {
if (state === "started") {
t.test(camera, camera.capabilities);
} else {
ok(false, "preview started (state = '" + state + "')");
}
camera.onPreviewStateChange = null;
};
}
CameraTest.run = function() {
navigator.mozCameras.getCamera(whichCamera, initialConfig, onSuccess, onError);

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

@ -30,4 +30,6 @@ interface CameraCapabilities
[Constant, Cached] readonly attribute double exposureCompensationStep;
[Constant, Cached] readonly attribute any recorderProfiles;
[Constant, Cached] readonly attribute sequence<DOMString> isoModes;
};

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

@ -231,6 +231,11 @@ interface CameraControl : MediaStream
[Throws]
readonly attribute unrestricted double exposureCompensation;
/* one of the values chosen from capabilities.isoModes; default
value is "auto" if supported. */
[Throws]
attribute DOMString isoMode;
/* the function to call on the camera's shutter event, to trigger
a shutter sound and/or a visual shutter indicator. */
attribute CameraShutterCallback? onShutter;

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

@ -12,6 +12,7 @@
#include "mozilla/layers/GrallocTextureClient.h"
#include "gfx2DGlue.h"
#include "gfxImageSurface.h"
#include "YCbCrUtils.h" // for YCbCr conversions
#include <OMX_IVCommon.h>
#include <ColorConverter.h>
@ -255,8 +256,7 @@ GrallocImage::DeprecatedGetAsSurface()
width, height);
return imageSurface.forget();
}
else if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
} else if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
uint32_t uvOffset = height * width;
ConvertYVU420SPToRGB565(buffer, width,
buffer + uvOffset, width,
@ -264,6 +264,13 @@ GrallocImage::DeprecatedGetAsSurface()
width, height);
return imageSurface.forget();
} else if (format == HAL_PIXEL_FORMAT_YV12) {
gfx::ConvertYCbCrToRGB(mData,
gfx::ImageFormatToSurfaceFormat(imageSurface->Format()),
mSize,
imageSurface->Data(),
imageSurface->Stride());
return imageSurface.forget();
}
android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat,
@ -346,8 +353,7 @@ GrallocImage::GetAsSourceSurface()
surface->Unmap();
return surface;
}
else if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
} else if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
uint32_t uvOffset = height * width;
ConvertYVU420SPToRGB565(buffer, width,
buffer + uvOffset, width,
@ -356,6 +362,13 @@ GrallocImage::GetAsSourceSurface()
surface->Unmap();
return surface;
} else if (format == HAL_PIXEL_FORMAT_YV12) {
gfx::ConvertYCbCrToRGB(mData,
surface->GetFormat(),
mSize,
surface->GetData(),
surface->Stride());
return surface;
}
android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat,

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

@ -21,7 +21,6 @@ using mozilla::ipc::CloseFileRunnable;
CloseFileRunnable::CloseFileRunnable(const FileDescriptor& aFileDescriptor)
: mFileDescriptor(aFileDescriptor)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aFileDescriptor.IsValid());
}
@ -41,8 +40,6 @@ NS_IMPL_ISUPPORTS1(CloseFileRunnable, nsIRunnable)
void
CloseFileRunnable::Dispatch()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIEventTarget> eventTarget =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
NS_ENSURE_TRUE_VOID(eventTarget);

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

@ -33,7 +33,7 @@ import java.util.List;
public class FilePicker implements GeckoEventListener {
private static final String LOGTAG = "GeckoFilePicker";
private static FilePicker sFilePicker;
private final Context mContext;
private final Context context;
public interface ResultHandler {
public void gotFile(String filename);
@ -46,7 +46,7 @@ public class FilePicker implements GeckoEventListener {
}
protected FilePicker(Context context) {
mContext = context;
this.context = context;
GeckoAppShell.getEventDispatcher().registerEventListener("FilePicker:Show", this);
}
@ -54,7 +54,8 @@ public class FilePicker implements GeckoEventListener {
public void handleMessage(String event, final JSONObject message) {
if (event.equals("FilePicker:Show")) {
String mimeType = "*/*";
String mode = message.optString("mode");
final String mode = message.optString("mode");
final int tabId = message.optInt("tabId", -1);
if ("mimeType".equals(mode))
mimeType = message.optString("mimeType");
@ -68,15 +69,17 @@ public class FilePicker implements GeckoEventListener {
} catch (JSONException ex) {
Log.i(LOGTAG, "Can't add filename to message " + filename);
}
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
"FilePicker:Result", message.toString()));
}
});
}, tabId);
}
}
private void addActivities(Intent intent, HashMap<String, Intent> intents, HashMap<String, Intent> filters) {
PackageManager pm = mContext.getPackageManager();
PackageManager pm = context.getPackageManager();
List<ResolveInfo> lri = pm.queryIntentActivities(intent, 0);
for (ResolveInfo ri : lri) {
ComponentName cn = new ComponentName(ri.activityInfo.applicationInfo.packageName, ri.activityInfo.name);
@ -155,13 +158,13 @@ public class FilePicker implements GeckoEventListener {
private String getFilePickerTitle(String mimeType) {
if (mimeType.equals("audio/*")) {
return mContext.getString(R.string.filepicker_audio_title);
return context.getString(R.string.filepicker_audio_title);
} else if (mimeType.equals("image/*")) {
return mContext.getString(R.string.filepicker_image_title);
return context.getString(R.string.filepicker_image_title);
} else if (mimeType.equals("video/*")) {
return mContext.getString(R.string.filepicker_video_title);
return context.getString(R.string.filepicker_video_title);
} else {
return mContext.getString(R.string.filepicker_title);
return context.getString(R.string.filepicker_title);
}
}
@ -201,8 +204,8 @@ public class FilePicker implements GeckoEventListener {
* sends the file returned to the passed in handler. If a null handler is passed in, will still
* pick and launch the file picker, but will throw away the result.
*/
protected void showFilePickerAsync(String mimeType, final ResultHandler handler) {
final FilePickerResultHandler fileHandler = new FilePickerResultHandler(handler);
protected void showFilePickerAsync(String mimeType, final ResultHandler handler, final int tabId) {
final FilePickerResultHandler fileHandler = new FilePickerResultHandler(handler, context, tabId);
getFilePickerIntentAsync(mimeType, fileHandler, new IntentHandler() {
@Override
public void gotIntent(Intent intent) {

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

@ -6,9 +6,11 @@ package org.mozilla.gecko;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.util.ActivityResultHandler;
import org.mozilla.gecko.util.ThreadUtils;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@ -21,8 +23,10 @@ import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import android.os.Process;
import java.io.File;
import java.io.FileOutputStream;
@ -32,31 +36,27 @@ import java.util.Queue;
class FilePickerResultHandler implements ActivityResultHandler {
private static final String LOGTAG = "GeckoFilePickerResultHandler";
private static final String UPLOADS_DIR = "uploads";
protected final Queue<String> mFilePickerResult;
protected final FilePicker.ResultHandler mHandler;
protected final FilePicker.ResultHandler handler;
private final int tabId;
private final File cacheDir;
// this code is really hacky and doesn't belong anywhere so I'm putting it here for now
// until I can come up with a better solution.
private String mImageName = "";
public FilePickerResultHandler(Queue<String> resultQueue) {
mFilePickerResult = resultQueue;
mHandler = null;
}
/* Use this constructor to asynchronously listen for results */
public FilePickerResultHandler(FilePicker.ResultHandler handler) {
mFilePickerResult = null;
mHandler = handler;
public FilePickerResultHandler(final FilePicker.ResultHandler handler, final Context context, final int tabId) {
this.tabId = tabId;
cacheDir = new File(context.getCacheDir(), UPLOADS_DIR);
this.handler = handler;
}
private void sendResult(String res) {
if (mFilePickerResult != null)
mFilePickerResult.offer(res);
if (mHandler != null)
mHandler.gotFile(res);
if (handler != null) {
handler.gotFile(res);
}
}
@Override
@ -124,16 +124,16 @@ class FilePickerResultHandler implements ActivityResultHandler {
}
private class VideoLoaderCallbacks implements LoaderCallbacks<Cursor> {
final private Uri mUri;
final private Uri uri;
public VideoLoaderCallbacks(Uri uri) {
mUri = uri;
this.uri = uri;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
return new CursorLoader(fa,
mUri,
uri,
new String[] { MediaStore.Video.Media.DATA },
null, // selection
null, // selectionArgs
@ -152,17 +152,20 @@ class FilePickerResultHandler implements ActivityResultHandler {
public void onLoaderReset(Loader<Cursor> loader) { }
}
private class FileLoaderCallbacks implements LoaderCallbacks<Cursor> {
final private Uri mUri;
private class FileLoaderCallbacks implements LoaderCallbacks<Cursor>,
Tabs.OnTabsChangedListener {
final private Uri uri;
private String tempFile;
public FileLoaderCallbacks(Uri uri) {
mUri = uri;
this.uri = uri;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
final FragmentActivity fa = (FragmentActivity) GeckoAppShell.getGeckoInterface().getActivity();
return new CursorLoader(fa,
mUri,
uri,
new String[] { OpenableColumns.DISPLAY_NAME },
null, // selection
null, // selectionArgs
@ -174,7 +177,7 @@ class FilePickerResultHandler implements ActivityResultHandler {
if (cursor.moveToFirst()) {
String name = cursor.getString(0);
// tmp filenames must be at least 3 characters long. Add a prefix to make sure that happens
String fileName = "tmp_";
String fileName = "tmp_" + Process.myPid() + "-";
String fileExt = null;
int period;
@ -183,7 +186,7 @@ class FilePickerResultHandler implements ActivityResultHandler {
// Generate an extension if we don't already have one
if (name == null || (period = name.lastIndexOf('.')) == -1) {
String mimeType = cr.getType(mUri);
String mimeType = cr.getType(uri);
fileExt = "." + GeckoAppShell.getExtensionFromMimeType(mimeType);
} else {
fileExt = name.substring(period);
@ -192,9 +195,11 @@ class FilePickerResultHandler implements ActivityResultHandler {
// Now write the data to the temp file
try {
File file = File.createTempFile(fileName, fileExt, GeckoLoader.getGREDir(GeckoAppShell.getContext()));
cacheDir.mkdir();
File file = File.createTempFile(fileName, fileExt, cacheDir);
FileOutputStream fos = new FileOutputStream(file);
InputStream is = cr.openInputStream(mUri);
InputStream is = cr.openInputStream(uri);
byte[] buf = new byte[4096];
int len = is.read(buf);
while (len != -1) {
@ -203,8 +208,12 @@ class FilePickerResultHandler implements ActivityResultHandler {
}
fos.close();
String path = file.getAbsolutePath();
sendResult((path == null) ? "" : path);
tempFile = file.getAbsolutePath();
sendResult((tempFile == null) ? "" : tempFile);
if (tabId > -1 && !TextUtils.isEmpty(tempFile)) {
Tabs.registerOnTabsChangedListener(this);
}
} catch(IOException ex) {
Log.i(LOGTAG, "Error writing file", ex);
}
@ -215,6 +224,36 @@ class FilePickerResultHandler implements ActivityResultHandler {
@Override
public void onLoaderReset(Loader<Cursor> loader) { }
/*Tabs.OnTabsChangedListener*/
// This cleans up our temp file. If it doesn't run, we just hope that Android
// will eventually does the cleanup for us.
@Override
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
if (tab.getId() != tabId) {
return;
}
if (msg == Tabs.TabEvents.LOCATION_CHANGE ||
msg == Tabs.TabEvents.CLOSED) {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
File f = new File(tempFile);
f.delete();
}
});
// We're already on the UIThread, but we have to post this back to the uithread to avoid
// modifying the listener array while its being iterated through.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
Tabs.unregisterOnTabsChangedListener(FileLoaderCallbacks.this);
}
});
}
}
}
}

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

@ -44,11 +44,13 @@ import org.mozilla.gecko.health.StubbedHealthRecorder;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.GeckoMenuInflater;
import org.mozilla.gecko.menu.MenuPanel;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.prompts.PromptService;
import org.mozilla.gecko.updater.UpdateService;
import org.mozilla.gecko.updater.UpdateServiceHelper;
import org.mozilla.gecko.util.ActivityResultHandler;
import org.mozilla.gecko.util.FileUtils;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.ThreadUtils;
@ -133,6 +135,7 @@ public abstract class GeckoApp
Tabs.OnTabsChangedListener
{
private static final String LOGTAG = "GeckoApp";
private static final int ONE_DAY_MS = 1000*60*60*24;
private static enum StartupAction {
NORMAL, /* normal application start */
@ -156,6 +159,7 @@ public abstract class GeckoApp
public static final String PREFS_OOM_EXCEPTION = "OOMException";
public static final String PREFS_VERSION_CODE = "versionCode";
public static final String PREFS_WAS_STOPPED = "wasStopped";
public static final String PREFS_CLEANUP_TEMP_FILES = "cleanupTempFiles";
public static final String SAVED_STATE_IN_BACKGROUND = "inBackground";
public static final String SAVED_STATE_PRIVATE_SESSION = "privateSession";
@ -2053,6 +2057,15 @@ public abstract class GeckoApp
if (rec != null) {
rec.recordSessionEnd("P", editor);
}
// If we haven't done it before, cleanup any old files in our old temp dir
if (prefs.getBoolean(GeckoApp.PREFS_CLEANUP_TEMP_FILES, true)) {
File tempDir = GeckoLoader.getGREDir(GeckoApp.this);
FileUtils.delTree(tempDir, new FileUtils.NameAndAgeFilter(null, ONE_DAY_MS), false);
editor.putBoolean(GeckoApp.PREFS_CLEANUP_TEMP_FILES, false);
}
editor.commit();
// In theory, the first browser session will not run long enough that we need to

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

@ -534,6 +534,7 @@ sync_java_files = [
'background/healthreport/upload/ObsoleteDocumentTracker.java',
'background/healthreport/upload/SubmissionClient.java',
'background/healthreport/upload/SubmissionPolicy.java',
'background/nativecode/NativeCrypto.java',
'background/preferences/PreferenceFragment.java',
'background/preferences/PreferenceManagerCompat.java',
'browserid/ASNUtils.java',

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

@ -0,0 +1,27 @@
/* 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/. */
package org.mozilla.gecko.background.nativecode;
import org.mozilla.gecko.mozglue.RobocopTarget;
import java.security.GeneralSecurityException;
@RobocopTarget
public class NativeCrypto {
static {
System.loadLibrary("mozglue");
}
/**
* Wrapper to perform PBKDF2-HMAC-SHA-256 in native code.
*/
public native static byte[] pbkdf2SHA256(byte[] password, byte[] salt, int c, int dkLen)
throws GeneralSecurityException;
/**
* Wrapper to perform SHA-1 in native code.
*/
public native static byte[] sha1(byte[] str);
}

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

@ -123,6 +123,11 @@ public class HomePanelPicker extends FragmentActivity {
}
}
if (availablePanels.isEmpty()) {
setContentView(R.layout.home_panel_picker_empty);
return;
}
final PickerAdapter adapter = (PickerAdapter) mListView.getAdapter();
adapter.updateFromPanelInfos(availablePanels);
}

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

@ -92,6 +92,7 @@
<!ENTITY pref_category_home_panels "Panels">
<!ENTITY pref_home_add_panel "Add panel">
<!ENTITY home_add_panel_title "Add new panel">
<!ENTITY home_add_panel_empty "Sorry, we couldn\'t find any panels for you to add.">
<!-- Localization note: These are shown in the left sidebar on tablets -->
<!ENTITY pref_header_customize "Customize">

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

@ -48,6 +48,7 @@ gujar.sources += [
'util/ActivityResultHandler.java',
'util/ActivityResultHandlerMap.java',
'util/Clipboard.java',
'util/FileUtils.java',
'util/FloatUtils.java',
'util/GamepadUtils.java',
'util/GeckoBackgroundThread.java',

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

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="15dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/home_add_panel_empty" />

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

@ -115,6 +115,7 @@
<string name="pref_category_home_panels">&pref_category_home_panels;</string>
<string name="pref_home_add_panel">&pref_home_add_panel;</string>
<string name="home_add_panel_title">&home_add_panel_title;</string>
<string name="home_add_panel_empty">&home_add_panel_empty;</string>
<string name="pref_header_customize">&pref_header_customize;</string>
<string name="pref_header_display">&pref_header_display;</string>

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

@ -4,30 +4,14 @@
package org.mozilla.gecko.sync.crypto;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
public class PBKDF2 {
public static byte[] pbkdf2SHA1(byte[] password, byte[] salt, int c, int dkLen)
throws GeneralSecurityException {
// Won't work on API level 8, but this is trivial.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec keySpec;
try {
keySpec = new PBEKeySpec(new String(password, "UTF-8").toCharArray(), salt, c, dkLen * 8);
} catch (UnsupportedEncodingException e) {
throw new GeneralSecurityException(e);
}
SecretKey key = factory.generateSecret(keySpec);
return key.getEncoded();
}
public static byte[] pbkdf2SHA256(byte[] password, byte[] salt, int c, int dkLen)
throws GeneralSecurityException {
final String algorithm = "HmacSHA256";
@ -36,12 +20,18 @@ public class PBKDF2 {
prf.init(keyspec);
int hLen = prf.getMacLength();
byte U_r[] = new byte[hLen];
byte U_i[] = new byte[salt.length + 4];
byte scratch[] = new byte[hLen];
int l = Math.max(dkLen, hLen);
int r = dkLen - (l - 1) * hLen;
byte T[] = new byte[l * hLen];
int ti_offset = 0;
for (int i = 1; i <= l; i++) {
F(T, ti_offset, prf, salt, c, i);
Arrays.fill(U_r, (byte) 0);
F(T, ti_offset, prf, salt, c, i, U_r, U_i, scratch);
ti_offset += hLen;
}
@ -55,17 +45,18 @@ public class PBKDF2 {
return T;
}
private static void F(byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex) {
private static void F(byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex, byte U_r[], byte U_i[], byte[] scratch)
throws ShortBufferException, IllegalStateException {
final int hLen = prf.getMacLength();
byte U_r[] = new byte[hLen];
// U0 = S || INT (i);
byte U_i[] = new byte[S.length + 4];
System.arraycopy(S, 0, U_i, 0, S.length);
INT(U_i, S.length, blockIndex);
for (int i = 0; i < c; i++) {
U_i = prf.doFinal(U_i);
prf.update(U_i);
prf.doFinal(scratch, 0);
U_i = scratch;
xor(U_r, U_i);
}

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

@ -75,20 +75,20 @@ public class AboutHomeComponent extends BaseComponent {
assertVisible();
final int expectedPanelIndex = getPanelIndexForDevice(expectedPanel.ordinal());
assertEquals("The current HomePager panel is " + expectedPanel,
fAssertEquals("The current HomePager panel is " + expectedPanel,
expectedPanelIndex, getHomePagerView().getCurrentItem());
return this;
}
public AboutHomeComponent assertNotVisible() {
assertTrue("The HomePager is not visible",
fAssertTrue("The HomePager is not visible",
getHomePagerContainer().getVisibility() != View.VISIBLE ||
getHomePagerView().getVisibility() != View.VISIBLE);
return this;
}
public AboutHomeComponent assertVisible() {
assertTrue("The HomePager is visible",
fAssertTrue("The HomePager is visible",
getHomePagerContainer().getVisibility() == View.VISIBLE &&
getHomePagerView().getVisibility() == View.VISIBLE);
return this;
@ -96,7 +96,7 @@ public class AboutHomeComponent extends BaseComponent {
public AboutHomeComponent assertBannerNotVisible() {
View banner = getHomeBannerView();
assertTrue("The HomeBanner is not visible",
fAssertTrue("The HomeBanner is not visible",
getHomePagerContainer().getVisibility() != View.VISIBLE ||
banner.getVisibility() != View.VISIBLE ||
banner.getTranslationY() == banner.getHeight());
@ -104,7 +104,7 @@ public class AboutHomeComponent extends BaseComponent {
}
public AboutHomeComponent assertBannerVisible() {
assertTrue("The HomeBanner is visible",
fAssertTrue("The HomeBanner is visible",
getHomePagerContainer().getVisibility() == View.VISIBLE &&
getHomeBannerView().getVisibility() == View.VISIBLE);
return this;
@ -114,7 +114,7 @@ public class AboutHomeComponent extends BaseComponent {
assertBannerVisible();
final TextView textView = (TextView) getHomeBannerView().findViewById(R.id.text);
assertEquals("The correct HomeBanner text is shown",
fAssertEquals("The correct HomeBanner text is shown",
text, textView.getText().toString());
return this;
}
@ -148,7 +148,7 @@ public class AboutHomeComponent extends BaseComponent {
}
private void swipeToPanel(final int panelDirection) {
assertTrue("Swiping in a valid direction",
fAssertTrue("Swiping in a valid direction",
panelDirection == Solo.LEFT || panelDirection == Solo.RIGHT);
assertVisible();

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

@ -49,7 +49,7 @@ public class AppMenuComponent extends BaseComponent {
}
private void assertMenuIsNotOpen() {
assertFalse("Menu is not open", isMenuOpen());
fAssertFalse("Menu is not open", isMenuOpen());
}
private View getOverflowMenuButtonView() {
@ -90,8 +90,8 @@ public class AppMenuComponent extends BaseComponent {
final View menuItemView = findAppMenuItemView(text);
if (menuItemView != null) {
assertTrue("The menu item is enabled", menuItemView.isEnabled());
assertEquals("The menu item is visible", View.VISIBLE, menuItemView.getVisibility());
fAssertTrue("The menu item is enabled", menuItemView.isEnabled());
fAssertEquals("The menu item is visible", View.VISIBLE, menuItemView.getVisibility());
mSolo.clickOnView(menuItemView);
} else {
@ -120,8 +120,8 @@ public class AppMenuComponent extends BaseComponent {
private void pressOverflowMenuButton() {
final View overflowMenuButton = getOverflowMenuButtonView();
assertTrue("The overflow menu button is enabled", overflowMenuButton.isEnabled());
assertEquals("The overflow menu button is visible", View.VISIBLE, overflowMenuButton.getVisibility());
fAssertTrue("The overflow menu button is enabled", overflowMenuButton.isEnabled());
fAssertEquals("The overflow menu button is visible", View.VISIBLE, overflowMenuButton.getVisibility());
mSolo.clickOnView(overflowMenuButton, true);
}

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

@ -63,7 +63,7 @@ public class GeckoViewComponent extends BaseComponent {
private InputMethodManager getInputMethodManager() {
final InputMethodManager imm = (InputMethodManager)
mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
assertNotNull("Must have an InputMethodManager", imm);
fAssertNotNull("Must have an InputMethodManager", imm);
return imm;
}
@ -75,7 +75,7 @@ public class GeckoViewComponent extends BaseComponent {
}
public TextInput assertActive() {
assertTrue("Current view should be the active input view", isActive());
fAssertTrue("Current view should be the active input view", isActive());
return this;
}
@ -101,7 +101,7 @@ public class GeckoViewComponent extends BaseComponent {
}
public TextInput assertInputConnection() {
assertTrue("Current view should have an active InputConnection", hasInputConnection());
fAssertTrue("Current view should have an active InputConnection", hasInputConnection());
return this;
}
@ -127,7 +127,7 @@ public class GeckoViewComponent extends BaseComponent {
*/
public TextInput testInputConnection(final InputConnectionTest test) {
assertNotNull("Test must not be null", test);
fAssertNotNull("Test must not be null", test);
assertInputConnection();
// GeckoInputConnection can run on another thread than the main thread,
@ -165,7 +165,7 @@ public class GeckoViewComponent extends BaseComponent {
// InputConnection thread. Therefore, the InputConnection thread must not be
// the same as the instrumentation thread to avoid a deadlock. This should
// always be the case and we perform a sanity check to make sure.
assertNotSame("InputConnection should not be running on instrumentation thread",
fAssertNotSame("InputConnection should not be running on instrumentation thread",
Looper.myLooper(), inputConnectionHandler.getLooper());
mDone = false;
@ -183,7 +183,7 @@ public class GeckoViewComponent extends BaseComponent {
public void run() {
final EditorInfo info = new EditorInfo();
final InputConnection ic = getView().onCreateInputConnection(info);
assertNotNull("Must have an InputConnection", ic);
fAssertNotNull("Must have an InputConnection", ic);
// Restore the IC to a clean state
ic.clearMetaKeyStates(-1);
ic.finishComposingText();

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

@ -28,23 +28,23 @@ public class ToolbarComponent extends BaseComponent {
}
public ToolbarComponent assertIsEditing() {
assertTrue("The toolbar is in the editing state", isEditing());
fAssertTrue("The toolbar is in the editing state", isEditing());
return this;
}
public ToolbarComponent assertIsNotEditing() {
assertFalse("The toolbar is not in the editing state", isEditing());
fAssertFalse("The toolbar is not in the editing state", isEditing());
return this;
}
public ToolbarComponent assertTitle(final String expected) {
assertEquals("The Toolbar title is " + expected, expected, getTitle());
fAssertEquals("The Toolbar title is " + expected, expected, getTitle());
return this;
}
public ToolbarComponent assertUrl(final String expected) {
assertIsEditing();
assertEquals("The Toolbar url is " + expected, expected, getUrlEditText().getText());
fAssertEquals("The Toolbar url is " + expected, expected, getUrlEditText().getText());
return this;
}
@ -158,12 +158,12 @@ public class ToolbarComponent extends BaseComponent {
}
public ToolbarComponent enterUrl(final String url) {
assertNotNull("url is not null", url);
fAssertNotNull("url is not null", url);
assertIsEditing();
final EditText urlEditText = getUrlEditText();
assertTrue("The UrlEditText is the input method target",
fAssertTrue("The UrlEditText is the input method target",
urlEditText.isInputMethodTarget());
mSolo.clearEditText(urlEditText);
@ -183,9 +183,9 @@ public class ToolbarComponent extends BaseComponent {
}
private ToolbarComponent pressButton(final View view, final String buttonName) {
assertNotNull("The " + buttonName + " button View is not null", view);
assertTrue("The " + buttonName + " button is enabled", view.isEnabled());
assertEquals("The " + buttonName + " button is visible",
fAssertNotNull("The " + buttonName + " button View is not null", view);
fAssertTrue("The " + buttonName + " button is enabled", view.isEnabled());
fAssertEquals("The " + buttonName + " button is visible",
View.VISIBLE, view.getVisibility());
assertIsNotEditing();

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

@ -24,89 +24,89 @@ public final class AssertionHelper {
sAsserter = context.getAsserter();
}
public static void assertArrayEquals(final String message, final byte[] expecteds, final byte[] actuals) {
public static void fAssertArrayEquals(final String message, final byte[] expecteds, final byte[] actuals) {
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
}
public static void assertArrayEquals(final String message, final char[] expecteds, final char[] actuals) {
public static void fAssertArrayEquals(final String message, final char[] expecteds, final char[] actuals) {
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
}
public static void assertArrayEquals(final String message, final short[] expecteds, final short[] actuals) {
public static void fAssertArrayEquals(final String message, final short[] expecteds, final short[] actuals) {
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
}
public static void assertArrayEquals(final String message, final int[] expecteds, final int[] actuals) {
public static void fAssertArrayEquals(final String message, final int[] expecteds, final int[] actuals) {
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
}
public static void assertArrayEquals(final String message, final long[] expecteds, final long[] actuals) {
public static void fAssertArrayEquals(final String message, final long[] expecteds, final long[] actuals) {
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
}
public static void assertArrayEquals(final String message, final Object[] expecteds, final Object[] actuals) {
public static void fAssertArrayEquals(final String message, final Object[] expecteds, final Object[] actuals) {
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
}
public static void assertEquals(final String message, final double expected, final double actual, final double delta) {
public static void fAssertEquals(final String message, final double expected, final double actual, final double delta) {
if (Double.compare(expected, actual) != 0) {
sAsserter.ok(Math.abs(expected - actual) <= delta, message, DIAG_STRING);
}
}
public static void assertEquals(final String message, final long expected, final long actual) {
public static void fAssertEquals(final String message, final long expected, final long actual) {
sAsserter.is(actual, expected, message);
}
public static void assertEquals(final String message, final Object expected, final Object actual) {
public static void fAssertEquals(final String message, final Object expected, final Object actual) {
sAsserter.is(actual, expected, message);
}
public static void assertNotEquals(final String message, final double unexpected, final double actual, final double delta) {
public static void fAssertNotEquals(final String message, final double unexpected, final double actual, final double delta) {
sAsserter.ok(Math.abs(unexpected - actual) > delta, message, DIAG_STRING);
}
public static void assertNotEquals(final String message, final long unexpected, final long actual) {
public static void fAssertNotEquals(final String message, final long unexpected, final long actual) {
sAsserter.isnot(actual, unexpected, message);
}
public static void assertNotEquals(final String message, final Object unexpected, final Object actual) {
public static void fAssertNotEquals(final String message, final Object unexpected, final Object actual) {
sAsserter.isnot(actual, unexpected, message);
}
public static void assertFalse(final String message, final boolean actual) {
public static void fAssertFalse(final String message, final boolean actual) {
sAsserter.ok(!actual, message, DIAG_STRING);
}
public static void assertNotNull(final String message, final Object actual) {
public static void fAssertNotNull(final String message, final Object actual) {
sAsserter.isnot(actual, null, message);
}
public static void assertNotSame(final String message, final Object unexpected, final Object actual) {
public static void fAssertNotSame(final String message, final Object unexpected, final Object actual) {
sAsserter.ok(unexpected != actual, message, DIAG_STRING);
}
public static void assertNull(final String message, final Object actual) {
public static void fAssertNull(final String message, final Object actual) {
sAsserter.is(actual, null, message);
}
public static void assertSame(final String message, final Object expected, final Object actual) {
public static void fAssertSame(final String message, final Object expected, final Object actual) {
sAsserter.ok(expected == actual, message, DIAG_STRING);
}
public static void assertTrue(final String message, final boolean actual) {
public static void fAssertTrue(final String message, final boolean actual) {
sAsserter.ok(actual, message, DIAG_STRING);
}
public static void assertIsPixel(final String message, final int actual, final int r, final int g, final int b) {
public static void fAssertIsPixel(final String message, final int actual, final int r, final int g, final int b) {
sAsserter.ispixel(actual, r, g, b, message);
}
public static void assertIsNotPixel(final String message, final int actual, final int r, final int g, final int b) {
public static void fAssertIsNotPixel(final String message, final int actual, final int r, final int g, final int b) {
sAsserter.isnotpixel(actual, r, g, b, message);
}
public static void fail(final String message) {
public static void fFail(final String message) {
sAsserter.ok(false, message, DIAG_STRING);
}
}

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

@ -44,7 +44,7 @@ public final class DeviceHelper {
private DeviceHelper() { /* To disallow instantiation. */ }
public static void assertIsTablet() {
assertTrue("The device is a tablet", isTablet());
fAssertTrue("The device is a tablet", isTablet());
}
protected static void init(final UITestContext context) {

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

@ -53,10 +53,10 @@ public final class FrameworkHelper {
} catch (final NoSuchFieldException e) {
// We expect a valid field name; if it's not valid,
// the caller is doing something wrong and should be fixed.
fail("Argument field should be a valid field name: " + e.toString());
fFail("Argument field should be a valid field name: " + e.toString());
} catch (final IllegalAccessException e) {
// This should not happen. If it does, setAccessible above is not working.
fail("Field should be accessible: " + e.toString());
fFail("Field should be accessible: " + e.toString());
}
throw new IllegalStateException("Should not continue from previous failures");
}
@ -72,10 +72,10 @@ public final class FrameworkHelper {
} catch (final NoSuchFieldException e) {
// We expect a valid field name; if it's not valid,
// the caller is doing something wrong and should be fixed.
fail("Argument field should be a valid field name: " + e.toString());
fFail("Argument field should be a valid field name: " + e.toString());
} catch (final IllegalAccessException e) {
// This should not happen. If it does, setAccessible above is not working.
fail("Field should be accessible: " + e.toString());
fFail("Field should be accessible: " + e.toString());
}
throw new IllegalStateException("Cannot continue from previous failures");
}

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

@ -35,7 +35,7 @@ final public class NavigationHelper {
}
public static void enterAndLoadUrl(String url) {
assertNotNull("url is not null", url);
fAssertNotNull("url is not null", url);
url = adjustUrl(url);
sToolbar.enterEditingMode()
@ -47,7 +47,7 @@ final public class NavigationHelper {
* Returns a new URL with the docshell HTTP server host prefix.
*/
private static String adjustUrl(final String url) {
assertNotNull("url is not null", url);
fAssertNotNull("url is not null", url);
if (url.startsWith("about:") || url.startsWith("chrome:")) {
return url;

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

@ -32,7 +32,7 @@ public final class TextInputHelper {
public static void assertText(final String message,
final InputConnection ic,
final String text) {
assertEquals(message, text, getText(ic));
fAssertEquals(message, text, getText(ic));
}
public static void assertSelection(final String message,
@ -40,8 +40,8 @@ public final class TextInputHelper {
final int start,
final int end) {
ExtractedText extract = getExtractedText(ic);
assertEquals(message, start, extract.selectionStart);
assertEquals(message, end, extract.selectionEnd);
fAssertEquals(message, start, extract.selectionStart);
fAssertEquals(message, end, extract.selectionEnd);
}
public static void assertSelectionAt(final String message,
@ -56,9 +56,9 @@ public final class TextInputHelper {
final int start,
final int end) {
ExtractedText extract = getExtractedText(ic);
assertEquals(message, text, extract.text);
assertEquals(message, start, extract.selectionStart);
assertEquals(message, end, extract.selectionEnd);
fAssertEquals(message, text, extract.text);
fAssertEquals(message, start, extract.selectionStart);
fAssertEquals(message, end, extract.selectionEnd);
}
public static void assertTextAndSelectionAt(final String message,

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

@ -54,7 +54,7 @@ public final class WaitHelper {
*/
public static void waitFor(String message, final Condition condition) {
message = "Waiting for " + message + ".";
assertTrue(message, sSolo.waitForCondition(condition, DEFAULT_MAX_WAIT_MS));
fAssertTrue(message, sSolo.waitForCondition(condition, DEFAULT_MAX_WAIT_MS));
}
/**
@ -63,7 +63,7 @@ public final class WaitHelper {
*/
public static void waitFor(String message, final Condition condition, final int waitMillis) {
message = "Waiting for " + message + " with timeout " + waitMillis + ".";
assertTrue(message, sSolo.waitForCondition(condition, waitMillis));
fAssertTrue(message, sSolo.waitForCondition(condition, waitMillis));
}
/**
@ -71,7 +71,7 @@ public final class WaitHelper {
* that will perform the action that will cause the page to load.
*/
public static void waitForPageLoad(final Runnable initiatingAction) {
assertNotNull("initiatingAction is not null", initiatingAction);
fAssertNotNull("initiatingAction is not null", initiatingAction);
// Some changes to the UI occur in response to the same event we listen to for when
// the page has finished loading (e.g. a page title update). As such, we ensure this

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

@ -127,4 +127,5 @@ skip-if = android_version == "10"
# disabled on Android 2.3; bug 979597
skip-if = android_version == "10"
[testInputConnection]
[testNativeCrypto]
[testSessionHistory]

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

@ -0,0 +1,189 @@
/* 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/. */
package org.mozilla.gecko.tests;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.*;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.mozilla.gecko.background.nativecode.NativeCrypto;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.tests.helpers.*;
import android.os.SystemClock;
/**
* Tests the Java wrapper over native implementations of crypto code. Test vectors from:
* * PBKDF2SHA256:
* - <https://github.com/ircmaxell/PHP-PasswordLib/blob/master/test/Data/Vectors/pbkdf2-draft-josefsson-sha256.test-vectors>
- <https://gitorious.org/scrypt/nettle-scrypt/blobs/37c0d5288e991604fe33dba2f1724986a8dddf56/testsuite/pbkdf2-test.c>
* SHA-1:
- <http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c>
*/
public class testNativeCrypto extends UITest {
private final static String LOGTAG = "testNativeCrypto";
/**
* Robocop supports only a single test function per test class. Therefore, we
* have a single top-level test function that dispatches to sub-tests,
* accepting that we might fail part way through the cycle. Proper JUnit 3
* testing can't land soon enough!
*
* @throws Exception
*/
public void test() throws Exception {
// This test could complete very quickly. If it completes too soon, the
// minidumps directory may not be created before the process is
// taken down, causing bug 722166. But we can't run the test and then block
// for Gecko:Ready, since it may have arrived before we block. So we wait.
// Again, JUnit 3 can't land soon enough!
GeckoHelper.blockForReady();
_testPBKDF2SHA256A();
_testPBKDF2SHA256B();
_testPBKDF2SHA256C();
_testPBKDF2SHA256scryptA();
_testPBKDF2SHA256scryptB();
_testPBKDF2SHA256InvalidLenArg();
_testSHA1();
_testSHA1AgainstMessageDigest();
}
public void _testPBKDF2SHA256A() throws UnsupportedEncodingException, GeneralSecurityException {
final String p = "password";
final String s = "salt";
final int dkLen = 32;
checkPBKDF2SHA256(p, s, 1, dkLen, "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b");
checkPBKDF2SHA256(p, s, 4096, dkLen, "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a");
}
public void _testPBKDF2SHA256B() throws UnsupportedEncodingException, GeneralSecurityException {
final String p = "passwordPASSWORDpassword";
final String s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
final int dkLen = 40;
checkPBKDF2SHA256(p, s, 4096, dkLen, "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9");
}
public void _testPBKDF2SHA256scryptA() throws UnsupportedEncodingException, GeneralSecurityException {
final String p = "passwd";
final String s = "salt";
final int dkLen = 64;
checkPBKDF2SHA256(p, s, 1, dkLen, "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783");
}
public void _testPBKDF2SHA256scryptB() throws UnsupportedEncodingException, GeneralSecurityException {
final String p = "Password";
final String s = "NaCl";
final int dkLen = 64;
checkPBKDF2SHA256(p, s, 80000, dkLen, "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d");
}
public void _testPBKDF2SHA256C() throws UnsupportedEncodingException, GeneralSecurityException {
final String p = "pass\0word";
final String s = "sa\0lt";
final int dkLen = 16;
checkPBKDF2SHA256(p, s, 4096, dkLen, "89b69d0516f829893c696226650a8687");
}
public void _testPBKDF2SHA256InvalidLenArg() throws UnsupportedEncodingException, GeneralSecurityException {
final String p = "password";
final String s = "salt";
final int c = 1;
final int dkLen = -1; // Should always be positive.
try {
final byte[] key = NativeCrypto.pbkdf2SHA256(p.getBytes("US-ASCII"), s.getBytes("US-ASCII"), c, dkLen);
fFail("Expected sha256 to throw with negative dkLen argument.");
} catch (IllegalArgumentException e) { } // Expected.
}
private void _testSHA1() throws UnsupportedEncodingException {
final String[] inputs = new String[] {
"abc",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"" // To be filled in below.
};
final String baseStr = "01234567";
final int repetitions = 80;
final StringBuilder builder = new StringBuilder(baseStr.length() * repetitions);
for (int i = 0; i < 80; ++i) {
builder.append(baseStr);
}
inputs[2] = builder.toString();
final String[] expecteds = new String[] {
"a9993e364706816aba3e25717850c26c9cd0d89d",
"84983e441c3bd26ebaae4aa1f95129e5e54670f1",
"dea356a2cddd90c7a7ecedc5ebb563934f460452"
};
for (int i = 0; i < inputs.length; ++i) {
final byte[] input = inputs[i].getBytes("US-ASCII");
final String expected = expecteds[i];
final byte[] actual = NativeCrypto.sha1(input);
fAssertNotNull("Hashed value is non-null", actual);
assertExpectedBytes(expected, actual);
}
}
/**
* Test to ensure the output of our SHA1 algo is the same as MessageDigest's. This is important
* because we intend to replace MessageDigest in FHR with this SHA-1 algo (bug 959652).
*/
private void _testSHA1AgainstMessageDigest() throws UnsupportedEncodingException,
NoSuchAlgorithmException {
final String[] inputs = {
"password",
"saranghae",
"aoeusnthaoeusnthaoeusnth \0 12345098765432109876_!"
};
final MessageDigest digest = MessageDigest.getInstance("SHA-1");
for (final String input : inputs) {
final byte[] inputBytes = input.getBytes("US-ASCII");
final byte[] mdBytes = digest.digest(inputBytes);
final byte[] ourBytes = NativeCrypto.sha1(inputBytes);
fAssertArrayEquals("MessageDigest hash is the same as NativeCrypto SHA-1 hash", mdBytes, ourBytes);
}
}
private void checkPBKDF2SHA256(String p, String s, int c, int dkLen, final String expectedStr)
throws GeneralSecurityException, UnsupportedEncodingException {
final long start = SystemClock.elapsedRealtime();
final byte[] key = NativeCrypto.pbkdf2SHA256(p.getBytes("US-ASCII"), s.getBytes("US-ASCII"), c, dkLen);
fAssertNotNull("Hash result is non-null", key);
final long end = SystemClock.elapsedRealtime();
dumpLog(LOGTAG, "SHA-256 " + c + " took " + (end - start) + "ms");
if (expectedStr == null) {
return;
}
fAssertEquals("Hash result is the appropriate length", dkLen,
Utils.hex2Byte(expectedStr).length);
assertExpectedBytes(expectedStr, key);
}
private void assertExpectedBytes(final String expectedStr, byte[] key) {
fAssertEquals("Expected string matches hash result", expectedStr, Utils.byte2Hex(key));
final byte[] expected = Utils.hex2Byte(expectedStr);
fAssertEquals("Expected byte array length matches key length", expected.length, key.length);
fAssertArrayEquals("Expected byte array matches key byte array", expected, key);
}
}

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

@ -0,0 +1,81 @@
/* 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/. */
package org.mozilla.gecko.util;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.io.FilenameFilter;
public class FileUtils {
private static final String LOGTAG= "GeckoFileUtils";
/*
* A basic Filter for checking a filename and age.
**/
static public class NameAndAgeFilter implements FilenameFilter {
final private String mName;
final private double mMaxAge;
public NameAndAgeFilter(String name, double age) {
mName = name;
mMaxAge = age;
}
@Override
public boolean accept(File dir, String filename) {
if (mName == null || mName.matches(filename)) {
File f = new File(dir, filename);
if (mMaxAge < 0 || System.currentTimeMillis() - f.lastModified() > mMaxAge) {
return true;
}
}
return false;
}
}
public static void delTree(File dir, FilenameFilter filter, boolean recurse) {
String[] files = null;
if (filter != null) {
files = dir.list(filter);
} else {
files = dir.list();
}
if (files == null) {
return;
}
for (String file : files) {
File f = new File(dir, file);
delete(f, recurse);
}
}
public static boolean delete(File file) throws IOException {
return delete(file, true);
}
public static boolean delete(File file, boolean recurse) {
if (file.isDirectory() && recurse) {
// If the quick delete failed and this is a dir, recursively delete the contents of the dir
String files[] = file.list();
for (String temp : files) {
File fileDelete = new File(file, temp);
try {
delete(fileDelete);
} catch(IOException ex) {
Log.i(LOGTAG, "Error deleting " + fileDelete.getPath(), ex);
}
}
}
// Even if this is a dir, it should now be empty and delete should work
return file.delete();
}
}

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

@ -257,7 +257,7 @@ var SelectionHandler = {
// Clear out any existing active selection
this._closeSelection();
this._initTargetInfo(aElement);
this._initTargetInfo(aElement, this.TYPE_SELECTION);
// Clear any existing selection from the document
this._contentWindow.getSelection().removeAllRanges();
@ -510,7 +510,7 @@ var SelectionHandler = {
(aElement instanceof HTMLTextAreaElement)))
return;
this._initTargetInfo(aElement);
this._initTargetInfo(aElement, this.TYPE_CURSOR);
// Caret-specific observer/listeners
Services.obs.addObserver(this, "TextSelection:UpdateCaretPos", false);
@ -525,12 +525,14 @@ var SelectionHandler = {
},
// Target initialization for both TYPE_CURSOR and TYPE_SELECTION
_initTargetInfo: function sh_initTargetInfo(aElement) {
_initTargetInfo: function sh_initTargetInfo(aElement, aSelectionType) {
this._targetElement = aElement;
if (aElement instanceof Ci.nsIDOMNSEditableElement) {
// Blur the targetElement to force IME code to undo previous style compositions
// (visible underlines / etc generated by autoCorrection, autoSuggestion)
aElement.blur();
if (aSelectionType === this.TYPE_SELECTION) {
// Blur the targetElement to force IME code to undo previous style compositions
// (visible underlines / etc generated by autoCorrection, autoSuggestion)
aElement.blur();
}
// Ensure targetElement is now focused normally
aElement.focus();
}

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

@ -197,8 +197,18 @@ FilePicker.prototype = {
_sendMessage: function() {
let msg = {
type: "FilePicker:Show",
guid: this.guid
guid: this.guid,
};
// Knowing the window lets us destroy any temp files when the tab is closed
// Other consumers of the file picker may have to either wait for Android
// to clean up the temp dir (not guaranteed) or clean up after themselves.
let win = Services.wm.getMostRecentWindow('navigator:browser');
let tab = win.BrowserApp.getTabForWindow(this._domWin.top)
if (tab) {
msg.tabId = tab.id;
}
if (!this._extensionsFilter && !this._mimeTypeFilter) {
// If neither filters is set show anything we can.
msg.mode = "mimeType";

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

@ -43,6 +43,7 @@ BACKGROUND_TESTS_JAVA_FILES := \
src/helpers/DBHelpers.java \
src/helpers/DBProviderTestCase.java \
src/helpers/FakeProfileTestCase.java \
src/nativecode/test/TestNativeCrypto.java \
src/sync/helpers/BookmarkHelpers.java \
src/sync/helpers/DefaultBeginDelegate.java \
src/sync/helpers/DefaultCleanDelegate.java \

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

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.nativecode.test;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import junit.framework.TestCase;
import org.mozilla.gecko.background.nativecode.NativeCrypto;
import org.mozilla.gecko.sync.Utils;
// Test vectors from
// SHA-256: <https://github.com/ircmaxell/PHP-PasswordLib/blob/master/test/Data/Vectors/pbkdf2-draft-josefsson-sha256.test-vectors>
// <https://gitorious.org/scrypt/nettle-scrypt/blobs/37c0d5288e991604fe33dba2f1724986a8dddf56/testsuite/pbkdf2-test.c>
public class TestNativeCrypto extends TestCase {
public final void testPBKDF2SHA256A() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "password";
String s = "salt";
int dkLen = 32;
checkPBKDF2SHA256(p, s, 1, dkLen, "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b");
checkPBKDF2SHA256(p, s, 4096, dkLen, "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a");
}
public final void testPBKDF2SHA256B() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "passwordPASSWORDpassword";
String s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
int dkLen = 40;
checkPBKDF2SHA256(p, s, 4096, dkLen, "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9");
}
public final void testPBKDF2SHA256scryptA() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "passwd";
String s = "salt";
int dkLen = 64;
checkPBKDF2SHA256(p, s, 1, dkLen, "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783");
}
public final void testPBKDF2SHA256scryptB() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "Password";
String s = "NaCl";
int dkLen = 64;
checkPBKDF2SHA256(p, s, 80000, dkLen, "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d");
}
public final void testPBKDF2SHA256C() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "pass\0word";
String s = "sa\0lt";
int dkLen = 16;
checkPBKDF2SHA256(p, s, 4096, dkLen, "89b69d0516f829893c696226650a8687");
}
/*
// This test takes two or three minutes to run, so we don't.
public final void testPBKDF2SHA256D() throws UnsupportedEncodingException, GeneralSecurityException {
String p = "password";
String s = "salt";
int dkLen = 32;
checkPBKDF2SHA256(p, s, 16777216, dkLen, "cf81c66fe8cfc04d1f31ecb65dab4089f7f179e89b3b0bcb17ad10e3ac6eba46");
}
*/
public final void testTimePBKDF2SHA256() throws UnsupportedEncodingException, GeneralSecurityException {
checkPBKDF2SHA256("password", "salt", 80000, 32, null);
}
private void checkPBKDF2SHA256(String p, String s, int c, int dkLen,
final String expectedStr)
throws GeneralSecurityException, UnsupportedEncodingException {
long start = System.currentTimeMillis();
byte[] key = NativeCrypto.pbkdf2SHA256(p.getBytes("US-ASCII"), s.getBytes("US-ASCII"), c, dkLen);
assertNotNull(key);
long end = System.currentTimeMillis();
System.err.println("SHA-256 " + c + " took " + (end - start) + "ms");
if (expectedStr == null) {
return;
}
assertEquals(dkLen, Utils.hex2Byte(expectedStr).length);
assertExpectedBytes(expectedStr, key);
}
private void assertExpectedBytes(final String expectedStr, byte[] key) {
assertEquals(expectedStr, Utils.byte2Hex(key));
byte[] expected = Utils.hex2Byte(expectedStr);
assertEquals(expected.length, key.length);
for (int i = 0; i < key.length; i++) {
assertEquals(expected[i], key[i]);
}
}
}

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

@ -0,0 +1,74 @@
/* 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 "NativeCrypto.h"
#include <jni.h>
#include <errno.h>
#include <stdlib.h>
#include <inttypes.h>
#include "mozilla/SHA1.h"
#include "pbkdf2_sha256.h"
/**
* Helper function to invoke native PBKDF2 function with JNI
* arguments.
*/
extern "C" JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_pbkdf2SHA256
(JNIEnv *env, jclass jc, jbyteArray jpassword, jbyteArray jsalt, jint c, jint dkLen) {
if (dkLen < 0) {
env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
"dkLen should not be less than 0");
return NULL;
}
jbyte *password = env->GetByteArrayElements(jpassword, NULL);
size_t passwordLen = env->GetArrayLength(jpassword);
jbyte *salt = env->GetByteArrayElements(jsalt, NULL);
size_t saltLen = env->GetArrayLength(jsalt);
uint8_t hashResult[dkLen];
PBKDF2_SHA256((uint8_t *) password, passwordLen, (uint8_t *) salt, saltLen,
(uint64_t) c, hashResult, (size_t) dkLen);
env->ReleaseByteArrayElements(jpassword, password, JNI_ABORT);
env->ReleaseByteArrayElements(jsalt, salt, JNI_ABORT);
jbyteArray out = env->NewByteArray(dkLen);
if (out == NULL) {
return NULL;
}
env->SetByteArrayRegion(out, 0, dkLen, (jbyte *) hashResult);
return out;
}
using namespace mozilla;
/**
* Helper function to invoke native SHA-1 function with JNI arguments.
*/
extern "C" JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha1
(JNIEnv *env, jclass jc, jbyteArray jstr) {
jbyte *str = env->GetByteArrayElements(jstr, NULL);
size_t strLen = env->GetArrayLength(jstr);
SHA1Sum sha1;
SHA1Sum::Hash hashResult;
sha1.update((void *) str, (uint32_t) strLen);
sha1.finish(hashResult);
env->ReleaseByteArrayElements(jstr, str, JNI_ABORT);
jbyteArray out = env->NewByteArray(SHA1Sum::HashSize);
if (out == NULL) {
return NULL;
}
env->SetByteArrayRegion(out, 0, SHA1Sum::HashSize, (jbyte *) hashResult);
return out;
}

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

@ -0,0 +1,29 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_mozilla_gecko_background_nativecode_NativeCrypto */
#ifndef _Included_org_mozilla_gecko_background_nativecode_NativeCrypto
#define _Included_org_mozilla_gecko_background_nativecode_NativeCrypto
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_mozilla_gecko_background_nativecode_NativeCrypto
* Method: pbkdf2SHA256
* Signature: ([B[BII)[B
*/
JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_pbkdf2SHA256
(JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint);
/*
* Class: org_mozilla_gecko_background_nativecode_NativeCrypto
* Method: sha1
* Signature: ([B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha1
(JNIEnv *, jclass, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif

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

@ -10,8 +10,10 @@ EXPORTS += [
SOURCES += [
'APKOpen.cpp',
'NativeCrypto.cpp',
'nsGeckoUtils.cpp',
'NSSBridge.cpp',
'pbkdf2_sha256.c',
'SQLiteBridge.cpp',
]

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

@ -0,0 +1,432 @@
/*-
* Copyright 2005,2007,2009 Colin Percival
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <sys/endian.h>
#include "pbkdf2_sha256.h"
static inline uint32_t
be32dec(const void *pp)
{
const uint8_t *p = (uint8_t const *)pp;
return ((uint32_t)(p[3]) +
((uint32_t)(p[2]) << 8) +
((uint32_t)(p[1]) << 16) +
((uint32_t)(p[0]) << 24));
}
static inline void
be32enc(void *pp, uint32_t x)
{
uint8_t * p = (uint8_t *)pp;
p[3] = x & 0xff;
p[2] = (x >> 8) & 0xff;
p[1] = (x >> 16) & 0xff;
p[0] = (x >> 24) & 0xff;
}
/*
* Encode a length len/4 vector of (uint32_t) into a length len vector of
* (unsigned char) in big-endian form. Assumes len is a multiple of 4.
*/
static void
be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)
{
size_t i;
for (i = 0; i < len / 4; i++)
be32enc(dst + i * 4, src[i]);
}
/*
* Decode a big-endian length len vector of (unsigned char) into a length
* len/4 vector of (uint32_t). Assumes len is a multiple of 4.
*/
static void
be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)
{
size_t i;
for (i = 0; i < len / 4; i++)
dst[i] = be32dec(src + i * 4);
}
/* Elementary functions used by SHA256 */
#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
#define Maj(x, y, z) ((x & (y | z)) | (y & z))
#define SHR(x, n) (x >> n)
#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
/* SHA256 round function */
#define RND(a, b, c, d, e, f, g, h, k) \
t0 = h + S1(e) + Ch(e, f, g) + k; \
t1 = S0(a) + Maj(a, b, c); \
d += t0; \
h = t0 + t1;
/* Adjusted round function for rotating state */
#define RNDr(S, W, i, k) \
RND(S[(64 - i) % 8], S[(65 - i) % 8], \
S[(66 - i) % 8], S[(67 - i) % 8], \
S[(68 - i) % 8], S[(69 - i) % 8], \
S[(70 - i) % 8], S[(71 - i) % 8], \
W[i] + k)
/*
* SHA256 block compression function. The 256-bit state is transformed via
* the 512-bit input block to produce a new state.
*/
static void
SHA256_Transform(uint32_t * state, const unsigned char block[64])
{
uint32_t W[64];
uint32_t S[8];
uint32_t t0, t1;
int i;
/* 1. Prepare message schedule W. */
be32dec_vect(W, block, 64);
for (i = 16; i < 64; i++)
W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
/* 2. Initialize working variables. */
memcpy(S, state, 32);
/* 3. Mix. */
RNDr(S, W, 0, 0x428a2f98);
RNDr(S, W, 1, 0x71374491);
RNDr(S, W, 2, 0xb5c0fbcf);
RNDr(S, W, 3, 0xe9b5dba5);
RNDr(S, W, 4, 0x3956c25b);
RNDr(S, W, 5, 0x59f111f1);
RNDr(S, W, 6, 0x923f82a4);
RNDr(S, W, 7, 0xab1c5ed5);
RNDr(S, W, 8, 0xd807aa98);
RNDr(S, W, 9, 0x12835b01);
RNDr(S, W, 10, 0x243185be);
RNDr(S, W, 11, 0x550c7dc3);
RNDr(S, W, 12, 0x72be5d74);
RNDr(S, W, 13, 0x80deb1fe);
RNDr(S, W, 14, 0x9bdc06a7);
RNDr(S, W, 15, 0xc19bf174);
RNDr(S, W, 16, 0xe49b69c1);
RNDr(S, W, 17, 0xefbe4786);
RNDr(S, W, 18, 0x0fc19dc6);
RNDr(S, W, 19, 0x240ca1cc);
RNDr(S, W, 20, 0x2de92c6f);
RNDr(S, W, 21, 0x4a7484aa);
RNDr(S, W, 22, 0x5cb0a9dc);
RNDr(S, W, 23, 0x76f988da);
RNDr(S, W, 24, 0x983e5152);
RNDr(S, W, 25, 0xa831c66d);
RNDr(S, W, 26, 0xb00327c8);
RNDr(S, W, 27, 0xbf597fc7);
RNDr(S, W, 28, 0xc6e00bf3);
RNDr(S, W, 29, 0xd5a79147);
RNDr(S, W, 30, 0x06ca6351);
RNDr(S, W, 31, 0x14292967);
RNDr(S, W, 32, 0x27b70a85);
RNDr(S, W, 33, 0x2e1b2138);
RNDr(S, W, 34, 0x4d2c6dfc);
RNDr(S, W, 35, 0x53380d13);
RNDr(S, W, 36, 0x650a7354);
RNDr(S, W, 37, 0x766a0abb);
RNDr(S, W, 38, 0x81c2c92e);
RNDr(S, W, 39, 0x92722c85);
RNDr(S, W, 40, 0xa2bfe8a1);
RNDr(S, W, 41, 0xa81a664b);
RNDr(S, W, 42, 0xc24b8b70);
RNDr(S, W, 43, 0xc76c51a3);
RNDr(S, W, 44, 0xd192e819);
RNDr(S, W, 45, 0xd6990624);
RNDr(S, W, 46, 0xf40e3585);
RNDr(S, W, 47, 0x106aa070);
RNDr(S, W, 48, 0x19a4c116);
RNDr(S, W, 49, 0x1e376c08);
RNDr(S, W, 50, 0x2748774c);
RNDr(S, W, 51, 0x34b0bcb5);
RNDr(S, W, 52, 0x391c0cb3);
RNDr(S, W, 53, 0x4ed8aa4a);
RNDr(S, W, 54, 0x5b9cca4f);
RNDr(S, W, 55, 0x682e6ff3);
RNDr(S, W, 56, 0x748f82ee);
RNDr(S, W, 57, 0x78a5636f);
RNDr(S, W, 58, 0x84c87814);
RNDr(S, W, 59, 0x8cc70208);
RNDr(S, W, 60, 0x90befffa);
RNDr(S, W, 61, 0xa4506ceb);
RNDr(S, W, 62, 0xbef9a3f7);
RNDr(S, W, 63, 0xc67178f2);
/* 4. Mix local working variables into global state. */
for (i = 0; i < 8; i++)
state[i] += S[i];
/* Clean the stack. */
memset(W, 0, 256);
memset(S, 0, 32);
t0 = t1 = 0;
}
static unsigned char PAD[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* Add padding and terminating bit-count. */
static void
SHA256_Pad(SHA256_CTX * ctx)
{
unsigned char len[8];
uint32_t r, plen;
/*
* Convert length to a vector of bytes -- we do this now rather
* than later because the length will change after we pad.
*/
be32enc_vect(len, ctx->count, 8);
/* Add 1--64 bytes so that the resulting length is 56 mod 64. */
r = (ctx->count[1] >> 3) & 0x3f;
plen = (r < 56) ? (56 - r) : (120 - r);
SHA256_Update(ctx, PAD, (size_t)plen);
/* Add the terminating bit-count. */
SHA256_Update(ctx, len, 8);
}
/* SHA-256 initialization. Begins a SHA-256 operation. */
void
SHA256_Init(SHA256_CTX * ctx)
{
/* Zero bits processed so far. */
ctx->count[0] = ctx->count[1] = 0;
/* Magic initialization constants. */
ctx->state[0] = 0x6A09E667;
ctx->state[1] = 0xBB67AE85;
ctx->state[2] = 0x3C6EF372;
ctx->state[3] = 0xA54FF53A;
ctx->state[4] = 0x510E527F;
ctx->state[5] = 0x9B05688C;
ctx->state[6] = 0x1F83D9AB;
ctx->state[7] = 0x5BE0CD19;
}
/* Add bytes into the hash. */
void
SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)
{
uint32_t bitlen[2];
uint32_t r;
const unsigned char *src = in;
/* Number of bytes left in the buffer from previous updates. */
r = (ctx->count[1] >> 3) & 0x3f;
/* Convert the length into a number of bits. */
bitlen[1] = ((uint32_t)len) << 3;
bitlen[0] = (uint32_t)(len >> 29);
/* Update number of bits. */
if ((ctx->count[1] += bitlen[1]) < bitlen[1])
ctx->count[0]++;
ctx->count[0] += bitlen[0];
/* Handle the case where we don't need to perform any transforms. */
if (len < 64 - r) {
memcpy(&ctx->buf[r], src, len);
return;
}
/* Finish the current block. */
memcpy(&ctx->buf[r], src, 64 - r);
SHA256_Transform(ctx->state, ctx->buf);
src += 64 - r;
len -= 64 - r;
/* Perform complete blocks. */
while (len >= 64) {
SHA256_Transform(ctx->state, src);
src += 64;
len -= 64;
}
/* Copy left over data into buffer. */
memcpy(ctx->buf, src, len);
}
/*
* SHA-256 finalization. Pads the input data, exports the hash value,
* and clears the context state.
*/
void
SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
{
/* Add padding. */
SHA256_Pad(ctx);
/* Write the hash. */
be32enc_vect(digest, ctx->state, 32);
/* Clear the context state. */
memset((void *)ctx, 0, sizeof(*ctx));
}
/* Initialize an HMAC-SHA256 operation with the given key. */
void
HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen)
{
unsigned char pad[64];
unsigned char khash[32];
const unsigned char * K = _K;
size_t i;
/* If Klen > 64, the key is really SHA256(K). */
if (Klen > 64) {
SHA256_Init(&ctx->ictx);
SHA256_Update(&ctx->ictx, K, Klen);
SHA256_Final(khash, &ctx->ictx);
K = khash;
Klen = 32;
}
/* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */
SHA256_Init(&ctx->ictx);
memset(pad, 0x36, 64);
for (i = 0; i < Klen; i++)
pad[i] ^= K[i];
SHA256_Update(&ctx->ictx, pad, 64);
/* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */
SHA256_Init(&ctx->octx);
memset(pad, 0x5c, 64);
for (i = 0; i < Klen; i++)
pad[i] ^= K[i];
SHA256_Update(&ctx->octx, pad, 64);
/* Clean the stack. */
memset(khash, 0, 32);
}
/* Add bytes to the HMAC-SHA256 operation. */
void
HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void *in, size_t len)
{
/* Feed data to the inner SHA256 operation. */
SHA256_Update(&ctx->ictx, in, len);
}
/* Finish an HMAC-SHA256 operation. */
void
HMAC_SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX * ctx)
{
unsigned char ihash[32];
/* Finish the inner SHA256 operation. */
SHA256_Final(ihash, &ctx->ictx);
/* Feed the inner hash to the outer SHA256 operation. */
SHA256_Update(&ctx->octx, ihash, 32);
/* Finish the outer SHA256 operation. */
SHA256_Final(digest, &ctx->octx);
/* Clean the stack. */
memset(ihash, 0, 32);
}
/**
* PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
* Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
* write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
*/
void
PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt,
size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen)
{
HMAC_SHA256_CTX PShctx, hctx;
size_t i;
uint8_t ivec[4];
uint8_t U[32];
uint8_t T[32];
uint64_t j;
int k;
size_t clen;
/* Compute HMAC state after processing P and S. */
HMAC_SHA256_Init(&PShctx, passwd, passwdlen);
HMAC_SHA256_Update(&PShctx, salt, saltlen);
/* Iterate through the blocks. */
for (i = 0; i * 32 < dkLen; i++) {
/* Generate INT(i + 1). */
be32enc(ivec, (uint32_t)(i + 1));
/* Compute U_1 = PRF(P, S || INT(i)). */
memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX));
HMAC_SHA256_Update(&hctx, ivec, 4);
HMAC_SHA256_Final(U, &hctx);
/* T_i = U_1 ... */
memcpy(T, U, 32);
for (j = 2; j <= c; j++) {
/* Compute U_j. */
HMAC_SHA256_Init(&hctx, passwd, passwdlen);
HMAC_SHA256_Update(&hctx, U, 32);
HMAC_SHA256_Final(U, &hctx);
/* ... xor U_j ... */
for (k = 0; k < 32; k++)
T[k] ^= U[k];
}
/* Copy as many bytes as necessary into buf. */
clen = dkLen - i * 32;
if (clen > 32)
clen = 32;
memcpy(&buf[i * 32], T, clen);
}
/* Clean PShctx, since we never called _Final on it. */
memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX));
}

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

@ -0,0 +1,70 @@
/*-
* Copyright 2005,2007,2009 Colin Percival
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $
*/
#ifndef _SHA256_H_
#define _SHA256_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
#include <stdint.h>
typedef struct SHA256Context {
uint32_t state[8];
uint32_t count[2];
unsigned char buf[64];
} SHA256_CTX;
typedef struct HMAC_SHA256Context {
SHA256_CTX ictx;
SHA256_CTX octx;
} HMAC_SHA256_CTX;
void SHA256_Init(SHA256_CTX *);
void SHA256_Update(SHA256_CTX *, const void *, size_t);
void SHA256_Final(unsigned char [32], SHA256_CTX *);
void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t);
void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t);
void HMAC_SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *);
/**
* PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
* Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
* write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
*/
void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t,
uint64_t, uint8_t *, size_t);
#ifdef __cplusplus
}
#endif
#endif /* !_SHA256_H_ */