Merge mozilla-central to mozilla-inbound on a CLOSED TREE
--HG-- extra : amend_source : 42c86ef1edf2141b6ac81506dd8a31cd68818292
|
@ -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 |
Двоичные данные
browser/themes/shared/customizableui/customize-illustration.png
До Ширина: | Высота: | Размер: 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_ */
|