зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team. a=merge
This commit is contained in:
Коммит
c4df0e3dd3
|
@ -154,6 +154,12 @@
|
|||
flip="none"
|
||||
level="parent"/>
|
||||
|
||||
<panel id="DateTimePickerPanel"
|
||||
hidden="true"
|
||||
noautofocus="true"
|
||||
consumeoutsideclicks="false"
|
||||
level="parent"/>
|
||||
|
||||
<!-- for select dropdowns. The menupopup is what shows the list of options,
|
||||
and the popuponly menulist makes things like the menuactive attributes
|
||||
work correctly on the menupopup. ContentSelectDropdown expects the
|
||||
|
@ -1056,7 +1062,8 @@
|
|||
tabcontainer="tabbrowser-tabs"
|
||||
contentcontextmenu="contentAreaContextMenu"
|
||||
autocompletepopup="PopupAutoComplete"
|
||||
selectmenulist="ContentSelectDropdown"/>
|
||||
selectmenulist="ContentSelectDropdown"
|
||||
datetimepicker="DateTimePickerPanel"/>
|
||||
</vbox>
|
||||
<vbox id="browser-border-end" hidden="true" layer="true"/>
|
||||
</hbox>
|
||||
|
|
|
@ -930,6 +930,12 @@ addEventListener("unload", () => {
|
|||
RefreshBlocker.uninit();
|
||||
});
|
||||
|
||||
addMessageListener("AllowScriptsToClose", () => {
|
||||
content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.allowScriptsToClose();
|
||||
});
|
||||
|
||||
addEventListener("MozAfterPaint", function onFirstPaint() {
|
||||
removeEventListener("MozAfterPaint", onFirstPaint);
|
||||
sendAsyncMessage("Browser:FirstPaint");
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<xul:vbox flex="1" class="browserContainer">
|
||||
<xul:stack flex="1" class="browserStack" anonid="browserStack">
|
||||
<xul:browser anonid="initialBrowser" type="content-primary" message="true" messagemanagergroup="browsers"
|
||||
xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist"/>
|
||||
xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist,datetimepicker"/>
|
||||
</xul:stack>
|
||||
</xul:vbox>
|
||||
</xul:hbox>
|
||||
|
@ -1886,6 +1886,10 @@
|
|||
if (this.hasAttribute("selectmenulist"))
|
||||
b.setAttribute("selectmenulist", this.getAttribute("selectmenulist"));
|
||||
|
||||
if (this.hasAttribute("datetimepicker")) {
|
||||
b.setAttribute("datetimepicker", this.getAttribute("datetimepicker"));
|
||||
}
|
||||
|
||||
b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
|
||||
|
||||
if (aParams.relatedBrowser) {
|
||||
|
|
|
@ -13,8 +13,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
|||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
var {
|
||||
EventManager,
|
||||
promiseObserved,
|
||||
} = ExtensionUtils;
|
||||
|
||||
function onXULFrameLoaderCreated({target}) {
|
||||
target.messageManager.sendAsyncMessage("AllowScriptsToClose", {});
|
||||
}
|
||||
|
||||
extensions.registerSchemaAPI("windows", "addon_parent", context => {
|
||||
let {extension} = context;
|
||||
return {
|
||||
|
@ -94,6 +99,10 @@ extensions.registerSchemaAPI("windows", "addon_parent", context => {
|
|||
return Promise.reject({message: "`tabId` may not be used in conjunction with `url`"});
|
||||
}
|
||||
|
||||
if (createData.allowScriptsToClose) {
|
||||
return Promise.reject({message: "`tabId` may not be used in conjunction with `allowScriptsToClose`"});
|
||||
}
|
||||
|
||||
let tab = TabManager.getTab(createData.tabId, context);
|
||||
|
||||
// Private browsing tabs can only be moved to private browsing
|
||||
|
@ -136,6 +145,11 @@ extensions.registerSchemaAPI("windows", "addon_parent", context => {
|
|||
}
|
||||
}
|
||||
|
||||
let {allowScriptsToClose, url} = createData;
|
||||
if (allowScriptsToClose === null) {
|
||||
allowScriptsToClose = typeof url === "string" && url.startsWith("moz-extension://");
|
||||
}
|
||||
|
||||
let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank",
|
||||
features.join(","), args);
|
||||
|
||||
|
@ -151,23 +165,22 @@ extensions.registerSchemaAPI("windows", "addon_parent", context => {
|
|||
(createData.state == "fullscreen" && AppConstants.platform != "macosx")) {
|
||||
window.document.documentElement.setAttribute("sizemode", createData.state);
|
||||
} else if (createData.state !== null) {
|
||||
// window.minimize() has no useful effect until the window has
|
||||
// been shown.
|
||||
|
||||
let obs = doc => {
|
||||
if (doc === window.document) {
|
||||
Services.obs.removeObserver(obs, "document-shown");
|
||||
WindowManager.setState(window, createData.state);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(obs, "document-shown", false);
|
||||
return;
|
||||
// window.minimize() has no effect until the window has been shown.
|
||||
return promiseObserved("document-shown", doc => doc == window.document).then(() => {
|
||||
WindowManager.setState(window, createData.state);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}).then(() => {
|
||||
if (allowScriptsToClose) {
|
||||
for (let {linkedBrowser} of window.gBrowser.tabs) {
|
||||
onXULFrameLoaderCreated({target: linkedBrowser});
|
||||
linkedBrowser.addEventListener( // eslint-disable-line mozilla/balanced-listeners
|
||||
"XULFrameLoaderCreated", onXULFrameLoaderCreated);
|
||||
}
|
||||
}
|
||||
return WindowManager.convert(extension, window);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -328,6 +328,11 @@
|
|||
"$ref": "WindowState",
|
||||
"optional": true,
|
||||
"description": "The initial state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
|
||||
},
|
||||
"allowScriptsToClose": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Allow scripts to close the window."
|
||||
}
|
||||
},
|
||||
"optional": true
|
||||
|
|
|
@ -165,3 +165,60 @@ add_task(function* testWindowCreateParams() {
|
|||
yield extension.unload();
|
||||
});
|
||||
|
||||
// Tests allowScriptsToClose option
|
||||
add_task(function* test_allowScriptsToClose() {
|
||||
const files = {
|
||||
"dummy.html": "<meta charset=utf-8><script src=close.js></script>",
|
||||
"close.js": function() {
|
||||
window.close();
|
||||
if (!window.closed) {
|
||||
browser.test.sendMessage("close-failed");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function background() {
|
||||
browser.test.onMessage.addListener((msg, options) => {
|
||||
function listener(_, {status}, {url}) {
|
||||
if (status == "complete" && url == options.url) {
|
||||
browser.tabs.onUpdated.removeListener(listener);
|
||||
browser.tabs.executeScript({file: "close.js"});
|
||||
}
|
||||
}
|
||||
options.url = browser.runtime.getURL(options.url);
|
||||
browser.windows.create(options);
|
||||
if (msg === "create+execute") {
|
||||
browser.tabs.onUpdated.addListener(listener);
|
||||
}
|
||||
});
|
||||
browser.test.notifyPass();
|
||||
}
|
||||
|
||||
const example = "http://example.com/";
|
||||
const manifest = {permissions: ["tabs", example]};
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({files, background, manifest});
|
||||
yield SpecialPowers.pushPrefEnv({set: [["dom.allow_scripts_to_close_windows", false]]});
|
||||
|
||||
yield extension.startup();
|
||||
yield extension.awaitFinish();
|
||||
|
||||
extension.sendMessage("create", {url: "dummy.html"});
|
||||
let win = yield BrowserTestUtils.waitForNewWindow();
|
||||
yield BrowserTestUtils.windowClosed(win);
|
||||
info("script allowed to close the window");
|
||||
|
||||
extension.sendMessage("create+execute", {url: example});
|
||||
win = yield BrowserTestUtils.waitForNewWindow();
|
||||
yield extension.awaitMessage("close-failed");
|
||||
info("script prevented from closing the window");
|
||||
win.close();
|
||||
|
||||
extension.sendMessage("create+execute", {url: example, allowScriptsToClose: true});
|
||||
win = yield BrowserTestUtils.waitForNewWindow();
|
||||
yield BrowserTestUtils.windowClosed(win);
|
||||
info("script allowed to close the window");
|
||||
|
||||
yield SpecialPowers.popPrefEnv();
|
||||
yield extension.unload();
|
||||
});
|
||||
|
|
|
@ -33,6 +33,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", "@mozilla.org/alerts-s
|
|||
["ContentClick", "resource:///modules/ContentClick.jsm"],
|
||||
["ContentPrefServiceParent", "resource://gre/modules/ContentPrefServiceParent.jsm"],
|
||||
["ContentSearch", "resource:///modules/ContentSearch.jsm"],
|
||||
["DateTimePickerHelper", "resource://gre/modules/DateTimePickerHelper.jsm"],
|
||||
["DirectoryLinksProvider", "resource:///modules/DirectoryLinksProvider.jsm"],
|
||||
["Feeds", "resource:///modules/Feeds.jsm"],
|
||||
["FileUtils", "resource://gre/modules/FileUtils.jsm"],
|
||||
|
@ -1022,6 +1023,7 @@ BrowserGlue.prototype = {
|
|||
CaptivePortalWatcher.init();
|
||||
|
||||
AutoCompletePopup.init();
|
||||
DateTimePickerHelper.init();
|
||||
|
||||
this._firstWindowTelemetry(aWindow);
|
||||
this._firstWindowLoaded();
|
||||
|
@ -1053,6 +1055,7 @@ BrowserGlue.prototype = {
|
|||
webrtcUI.uninit();
|
||||
FormValidationHandler.uninit();
|
||||
AutoCompletePopup.uninit();
|
||||
DateTimePickerHelper.uninit();
|
||||
if (AppConstants.NIGHTLY_BUILD) {
|
||||
AddonWatcher.uninit();
|
||||
}
|
||||
|
|
|
@ -29,3 +29,4 @@ support-files =
|
|||
[browser_blobURLIsolation.js]
|
||||
[browser_imageCacheIsolation.js]
|
||||
[browser_sharedworker.js]
|
||||
[browser_httpauth.js]
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
let Cu = Components.utils;
|
||||
let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
|
||||
|
||||
let server = new HttpServer();
|
||||
server.registerPathHandler('/file.html', fileHandler);
|
||||
server.start(-1);
|
||||
|
||||
let BASE_URI = 'http://localhost:' + server.identity.primaryPort;
|
||||
let FILE_URI = BASE_URI + '/file.html';
|
||||
|
||||
let credentialQueue = [];
|
||||
|
||||
// Ask the user agent for authorization.
|
||||
function fileHandler(metadata, response) {
|
||||
if (!metadata.hasHeader("Authorization")) {
|
||||
response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
|
||||
response.setHeader("WWW-Authenticate", "Basic realm=\"User Visible Realm\"");
|
||||
return;
|
||||
}
|
||||
|
||||
// This will be "account:password" encoded in base64.
|
||||
credentialQueue.push(metadata.getHeader("Authorization"));
|
||||
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
let body = "<html><body></body></html>";
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
function onCommonDialogLoaded(subject) {
|
||||
// Submit random account and password
|
||||
let dialog = subject.Dialog;
|
||||
dialog.ui.loginTextbox.setAttribute("value", Math.random());
|
||||
dialog.ui.password1Textbox.setAttribute("value", Math.random());
|
||||
dialog.ui.button0.click();
|
||||
}
|
||||
|
||||
Services.obs.addObserver(onCommonDialogLoaded, "common-dialog-loaded", false);
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.obs.removeObserver(onCommonDialogLoaded, "common-dialog-loaded");
|
||||
server.stop(() => {
|
||||
server = null;
|
||||
});
|
||||
});
|
||||
|
||||
function getResult() {
|
||||
// If two targets are isolated, they should get different credentials.
|
||||
// Otherwise, the credentials will be cached and therefore the same.
|
||||
return credentialQueue.shift();
|
||||
}
|
||||
|
||||
IsolationTestTools.runTests(FILE_URI, getResult);
|
||||
|
|
@ -731,7 +731,7 @@ promise_test(function(t) {
|
|||
'opacity: 0 !important' });
|
||||
getComputedStyle(div).opacity;
|
||||
|
||||
div.animate({ opacity: [ 0, 1 ] }, 10 * MS_PER_SEC);
|
||||
div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
|
||||
|
||||
div.style.setProperty('opacity', '1', 'important');
|
||||
getComputedStyle(div).opacity;
|
||||
|
|
|
@ -272,6 +272,7 @@ GK_ATOM(dataType, "data-type")
|
|||
GK_ATOM(dateTime, "date-time")
|
||||
GK_ATOM(datasources, "datasources")
|
||||
GK_ATOM(datetime, "datetime")
|
||||
GK_ATOM(datetimebox, "datetimebox")
|
||||
GK_ATOM(dblclick, "dblclick")
|
||||
GK_ATOM(dd, "dd")
|
||||
GK_ATOM(debug, "debug")
|
||||
|
@ -1985,6 +1986,7 @@ GK_ATOM(colorControlFrame, "colorControlFrame")
|
|||
GK_ATOM(columnSetFrame, "ColumnSetFrame")
|
||||
GK_ATOM(comboboxControlFrame, "ComboboxControlFrame")
|
||||
GK_ATOM(comboboxDisplayFrame, "ComboboxDisplayFrame")
|
||||
GK_ATOM(dateTimeControlFrame, "DateTimeControlFrame")
|
||||
GK_ATOM(deckFrame, "DeckFrame")
|
||||
GK_ATOM(detailsFrame, "DetailsFrame")
|
||||
GK_ATOM(fieldSetFrame, "FieldSetFrame")
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<canvas id="canvas" width="150" height="150"></canvas>
|
||||
|
||||
<script>
|
||||
|
||||
var canvas = document.getElementById('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.transform(1,0.5,-0.5,1,30,10);
|
||||
ctx.fillStyle = '#f00';
|
||||
ctx.fillRect(0, 0, 100, 100);
|
||||
|
||||
</script>
|
||||
</body></html>
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<canvas id="canvas" width="150" height="150"></canvas>
|
||||
|
||||
<script>
|
||||
|
||||
var canvas = document.getElementById('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.transform(1,0.5,-0.5,1,30,10);
|
||||
setTimeout(function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var transform = ctx.mozCurrentTransform;
|
||||
ctx.mozCurrentTransform = transform;
|
||||
ctx.fillStyle = '#f00';
|
||||
ctx.fillRect(0, 0, 100, 100);
|
||||
document.documentElement.removeAttribute("class");
|
||||
}, 10)
|
||||
|
||||
</script>
|
||||
</body></html>
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<canvas id="canvas" width="150" height="150"></canvas>
|
||||
|
||||
<script>
|
||||
|
||||
var canvas = document.getElementById('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.transform(1,0.5,-0.5,1,30,10);
|
||||
setTimeout(function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var transform = ctx.mozCurrentTransformInverse;
|
||||
ctx.mozCurrentTransformInverse = transform;
|
||||
ctx.fillStyle = '#f00';
|
||||
ctx.fillRect(0, 0, 100, 100);
|
||||
document.documentElement.removeAttribute("class");
|
||||
}, 10)
|
||||
|
||||
</script>
|
||||
</body></html>
|
|
@ -166,3 +166,7 @@ fuzzy-if(azureSkia,16,2) fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.
|
|||
|
||||
# Canvas Filter Reftests
|
||||
include filters/reftest.list
|
||||
|
||||
# Bug 1305963
|
||||
== mozCurrentTransform.html mozCurrentTransform-ref.html
|
||||
== mozCurrentTransformInverse.html mozCurrentTransform-ref.html
|
||||
|
|
|
@ -649,6 +649,8 @@ HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
}
|
||||
}
|
||||
|
||||
mLastSelectedSource = nullptr;
|
||||
|
||||
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "nsIIOService.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsAttrValueOrString.h"
|
||||
#include "nsDateTimeControlFrame.h"
|
||||
|
||||
#include "nsPresState.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
|
@ -2707,6 +2708,82 @@ HTMLInputElement::MozSetDirectory(const nsAString& aDirectoryPath,
|
|||
SetFilesOrDirectories(array, true);
|
||||
}
|
||||
|
||||
void HTMLInputElement::GetDateTimeInputBoxValue(DateTimeValue& aValue)
|
||||
{
|
||||
if (NS_WARN_IF(!IsDateTimeInputType(mType)) || !mDateTimeInputBoxValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
aValue = *mDateTimeInputBoxValue;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::UpdateDateTimeInputBox(const DateTimeValue& aValue)
|
||||
{
|
||||
if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->SetValueFromPicker(aValue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::SetDateTimePickerState(bool aOpen)
|
||||
{
|
||||
if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->SetPickerState(aOpen);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::OpenDateTimePicker(const DateTimeValue& aInitialValue)
|
||||
{
|
||||
if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDateTimeInputBoxValue = new DateTimeValue(aInitialValue);
|
||||
nsContentUtils::DispatchChromeEvent(OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLInputElement*>(this),
|
||||
NS_LITERAL_STRING("MozOpenDateTimePicker"),
|
||||
true, true);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::UpdateDateTimePicker(const DateTimeValue& aValue)
|
||||
{
|
||||
if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDateTimeInputBoxValue = new DateTimeValue(aValue);
|
||||
nsContentUtils::DispatchChromeEvent(OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLInputElement*>(this),
|
||||
NS_LITERAL_STRING("MozUpdateDateTimePicker"),
|
||||
true, true);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::CloseDateTimePicker()
|
||||
{
|
||||
if (NS_WARN_IF(!IsDateTimeInputType(mType))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsContentUtils::DispatchChromeEvent(OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLInputElement*>(this),
|
||||
NS_LITERAL_STRING("MozCloseDateTimePicker"),
|
||||
true, true);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLInputElement::MozIsTextField(bool aExcludePassword)
|
||||
{
|
||||
|
@ -2733,6 +2810,28 @@ HTMLInputElement::GetOwnerNumberControl()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
HTMLInputElement*
|
||||
HTMLInputElement::GetOwnerDateTimeControl()
|
||||
{
|
||||
if (IsInNativeAnonymousSubtree() &&
|
||||
mType == NS_FORM_INPUT_TEXT &&
|
||||
GetParent() &&
|
||||
GetParent()->GetParent() &&
|
||||
GetParent()->GetParent()->GetParent() &&
|
||||
GetParent()->GetParent()->GetParent()->GetParent()) {
|
||||
// Yes, this is very very deep.
|
||||
HTMLInputElement* ownerDateTimeControl =
|
||||
HTMLInputElement::FromContentOrNull(
|
||||
GetParent()->GetParent()->GetParent()->GetParent());
|
||||
if (ownerDateTimeControl &&
|
||||
ownerDateTimeControl->mType == NS_FORM_INPUT_TIME) {
|
||||
return ownerDateTimeControl;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult)
|
||||
{
|
||||
|
@ -2740,6 +2839,19 @@ HTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::SetUserInput(const nsAString& aInput,
|
||||
const mozilla::Maybe<nsIPrincipal*>& aPrincipal) {
|
||||
MOZ_ASSERT(aPrincipal.isSome());
|
||||
|
||||
if (mType == NS_FORM_INPUT_FILE &&
|
||||
!nsContentUtils::IsSystemPrincipal(aPrincipal.value())) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetUserInput(aInput);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLInputElement::SetUserInput(const nsAString& aValue)
|
||||
{
|
||||
|
@ -3164,6 +3276,12 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue, uint32_t aFlags)
|
|||
if (frame) {
|
||||
frame->UpdateForValueChange();
|
||||
}
|
||||
} else if (mType == NS_FORM_INPUT_TIME &&
|
||||
!IsExperimentalMobileType(mType)) {
|
||||
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->UpdateInputBoxValue();
|
||||
}
|
||||
}
|
||||
if (!mParserCreating) {
|
||||
OnValueChanged(/* aNotify = */ true,
|
||||
|
@ -3466,6 +3584,15 @@ HTMLInputElement::Blur(ErrorResult& aError)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mType == NS_FORM_INPUT_TIME && !IsExperimentalMobileType(mType)) {
|
||||
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->HandleBlurEvent();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::Blur(aError);
|
||||
}
|
||||
|
||||
|
@ -3485,6 +3612,14 @@ HTMLInputElement::Focus(ErrorResult& aError)
|
|||
}
|
||||
}
|
||||
|
||||
if (mType == NS_FORM_INPUT_TIME && !IsExperimentalMobileType(mType)) {
|
||||
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->HandleFocusEvent();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mType != NS_FORM_INPUT_FILE) {
|
||||
nsGenericHTMLElement::Focus(aError);
|
||||
return;
|
||||
|
@ -3788,7 +3923,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
|
|||
// Experimental mobile types rely on the system UI to prevent users to not
|
||||
// set invalid values but we have to be extra-careful. Especially if the
|
||||
// option has been enabled on desktop.
|
||||
if (IsExperimentalMobileType(mType) || IsDateTimeInputType(mType)) {
|
||||
if (IsExperimentalMobileType(mType)) {
|
||||
nsAutoString aValue;
|
||||
GetValueInternal(aValue);
|
||||
nsresult rv =
|
||||
|
@ -3809,6 +3944,18 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
|
|||
}
|
||||
}
|
||||
|
||||
if (mType == NS_FORM_INPUT_TIME &&
|
||||
!IsExperimentalMobileType(mType) &&
|
||||
aVisitor.mEvent->mMessage == eFocus &&
|
||||
aVisitor.mEvent->mOriginalTarget == this) {
|
||||
// If original target is this and not the anonymous text control, we should
|
||||
// pass the focus to the anonymous text control.
|
||||
nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->HandleFocusEvent();
|
||||
}
|
||||
}
|
||||
|
||||
if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted()) {
|
||||
if (mNumberControlSpinnerIsSpinning) {
|
||||
// If the timer is running the user has depressed the mouse on one of the
|
||||
|
@ -6682,6 +6829,11 @@ HTMLInputElement::AddStates(EventStates aStates)
|
|||
HTMLInputElement* ownerNumberControl = GetOwnerNumberControl();
|
||||
if (ownerNumberControl) {
|
||||
ownerNumberControl->AddStates(focusStates);
|
||||
} else {
|
||||
HTMLInputElement* ownerDateTimeControl = GetOwnerDateTimeControl();
|
||||
if (ownerDateTimeControl) {
|
||||
ownerDateTimeControl->AddStates(focusStates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6698,6 +6850,11 @@ HTMLInputElement::RemoveStates(EventStates aStates)
|
|||
HTMLInputElement* ownerNumberControl = GetOwnerNumberControl();
|
||||
if (ownerNumberControl) {
|
||||
ownerNumberControl->RemoveStates(focusStates);
|
||||
} else {
|
||||
HTMLInputElement* ownerDateTimeControl = GetOwnerDateTimeControl();
|
||||
if (ownerDateTimeControl) {
|
||||
ownerDateTimeControl->RemoveStates(focusStates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6875,12 +7032,14 @@ HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, int32_t*
|
|||
#endif
|
||||
|
||||
if (mType == NS_FORM_INPUT_FILE ||
|
||||
mType == NS_FORM_INPUT_NUMBER) {
|
||||
mType == NS_FORM_INPUT_NUMBER ||
|
||||
mType == NS_FORM_INPUT_TIME) {
|
||||
if (aTabIndex) {
|
||||
// We only want our native anonymous child to be tabable to, not ourself.
|
||||
*aTabIndex = -1;
|
||||
}
|
||||
if (mType == NS_FORM_INPUT_NUMBER) {
|
||||
if (mType == NS_FORM_INPUT_NUMBER ||
|
||||
mType == NS_FORM_INPUT_TIME) {
|
||||
*aIsFocusable = true;
|
||||
} else {
|
||||
*aIsFocusable = defaultFocusable;
|
||||
|
|
|
@ -772,7 +772,24 @@ public:
|
|||
void MozSetFileArray(const Sequence<OwningNonNull<File>>& aFiles);
|
||||
void MozSetDirectory(const nsAString& aDirectoryPath, ErrorResult& aRv);
|
||||
|
||||
/*
|
||||
* The following functions are called from datetime picker to let input box
|
||||
* know the current state of the picker or to update the input box on changes.
|
||||
*/
|
||||
void GetDateTimeInputBoxValue(DateTimeValue& aValue);
|
||||
void UpdateDateTimeInputBox(const DateTimeValue& aValue);
|
||||
void SetDateTimePickerState(bool aOpen);
|
||||
|
||||
/*
|
||||
* The following functions are called from datetime input box XBL to control
|
||||
* and update the picker.
|
||||
*/
|
||||
void OpenDateTimePicker(const DateTimeValue& aInitialValue);
|
||||
void UpdateDateTimePicker(const DateTimeValue& aValue);
|
||||
void CloseDateTimePicker();
|
||||
|
||||
HTMLInputElement* GetOwnerNumberControl();
|
||||
HTMLInputElement* GetOwnerDateTimeControl();
|
||||
|
||||
void StartNumberControlSpinnerSpin();
|
||||
enum SpinnerStopState {
|
||||
|
@ -803,7 +820,8 @@ public:
|
|||
|
||||
nsIEditor* GetEditor();
|
||||
|
||||
// XPCOM SetUserInput() is OK
|
||||
void SetUserInput(const nsAString& aInput,
|
||||
const mozilla::Maybe<nsIPrincipal*>& aPrincipal);
|
||||
|
||||
// XPCOM GetPhonetic() is OK
|
||||
|
||||
|
@ -1471,6 +1489,12 @@ protected:
|
|||
*/
|
||||
Decimal mRangeThumbDragStartValue;
|
||||
|
||||
/**
|
||||
* Current value in the input box, in DateTimeValue dictionary format, see
|
||||
* HTMLInputElement.webidl for details.
|
||||
*/
|
||||
nsAutoPtr<DateTimeValue> mDateTimeInputBoxValue;
|
||||
|
||||
/**
|
||||
* The selection properties cache for number controls. This is needed because
|
||||
* the number controls don't recycle their text field, so the normal cache in
|
||||
|
@ -1561,7 +1585,8 @@ private:
|
|||
static bool MayFireChangeOnBlur(uint8_t aType) {
|
||||
return IsSingleLineTextControl(false, aType) ||
|
||||
aType == NS_FORM_INPUT_RANGE ||
|
||||
aType == NS_FORM_INPUT_NUMBER;
|
||||
aType == NS_FORM_INPUT_NUMBER ||
|
||||
aType == NS_FORM_INPUT_TIME;
|
||||
}
|
||||
|
||||
struct nsFilePickerFilter {
|
||||
|
|
|
@ -5710,26 +5710,27 @@ ImageContainer* HTMLMediaElement::GetImageContainer()
|
|||
return container ? container->GetImageContainer() : nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLMediaElement::MaybeCreateAudioChannelAgent()
|
||||
void
|
||||
HTMLMediaElement::CreateAudioChannelAgent()
|
||||
{
|
||||
if (!mAudioChannelAgent) {
|
||||
nsresult rv;
|
||||
mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(mAudioChannelAgent);
|
||||
mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(),
|
||||
static_cast<int32_t>(mAudioChannel),
|
||||
this);
|
||||
if (mAudioChannelAgent) {
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
|
||||
mAudioChannelAgent = new AudioChannelAgent();
|
||||
mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(),
|
||||
static_cast<int32_t>(mAudioChannel),
|
||||
this);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
|
||||
{
|
||||
// If we have an error, we are not playing.
|
||||
if (mError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It might be resumed from remote, we should keep the audio channel agent.
|
||||
if (IsSuspendedByAudioChannel()) {
|
||||
return true;
|
||||
|
@ -5740,11 +5741,6 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
|
|||
return false;
|
||||
}
|
||||
|
||||
// If we have an error, we are not playing.
|
||||
if (mError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should consider any bfcached page or inactive document as non-playing.
|
||||
if (!IsActive()) {
|
||||
return false;
|
||||
|
@ -5755,6 +5751,11 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
|
|||
return true;
|
||||
}
|
||||
|
||||
// If we are actually playing...
|
||||
if (IsCurrentlyPlaying()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are seeking, we consider it as playing
|
||||
if (mPlayingThroughTheAudioChannelBeforeSeek) {
|
||||
return true;
|
||||
|
@ -5765,7 +5766,7 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
|
|||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -5781,9 +5782,8 @@ HTMLMediaElement::UpdateAudioChannelPlayingState()
|
|||
return;
|
||||
}
|
||||
|
||||
if (MaybeCreateAudioChannelAgent()) {
|
||||
NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel);
|
||||
}
|
||||
CreateAudioChannelAgent();
|
||||
NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6381,20 +6381,8 @@ HTMLMediaElement::IsCurrentlyPlaying() const
|
|||
{
|
||||
// We have playable data, but we still need to check whether data is "real"
|
||||
// current data.
|
||||
if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
|
||||
!IsPlaybackEnded()) {
|
||||
|
||||
// Restart the video after ended, it needs to seek to the new position.
|
||||
// In b2g, the cache is not large enough to store whole video data, so we
|
||||
// need to download data again. In this case, although the ready state is
|
||||
// "HAVE_CURRENT_DATA", it is the previous old data. Actually we are not
|
||||
// yet have enough currently data.
|
||||
if (mDecoder && mDecoder->IsSeeking() && !mPlayingBeforeSeek) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
|
||||
!IsPlaybackEnded();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -48,6 +48,7 @@ class MediaResource;
|
|||
class MediaDecoder;
|
||||
class VideoFrameContainer;
|
||||
namespace dom {
|
||||
class AudioChannelAgent;
|
||||
class MediaKeys;
|
||||
class TextTrack;
|
||||
class TimeRanges;
|
||||
|
@ -1221,9 +1222,8 @@ protected:
|
|||
// Notifies the audio channel agent when the element starts or stops playing.
|
||||
void NotifyAudioChannelAgent(bool aPlaying);
|
||||
|
||||
// Creates the audio channel agent if needed. Returns true if the audio
|
||||
// channel agent is ready to be used.
|
||||
bool MaybeCreateAudioChannelAgent();
|
||||
// Creates the audio channel agent.
|
||||
void CreateAudioChannelAgent();
|
||||
|
||||
// Determine if the element should be paused because of suspend conditions.
|
||||
bool ShouldElementBePaused();
|
||||
|
@ -1618,7 +1618,7 @@ protected:
|
|||
bool mDisableVideo;
|
||||
|
||||
// An agent used to join audio channel service.
|
||||
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
|
||||
RefPtr<AudioChannelAgent> mAudioChannelAgent;
|
||||
|
||||
RefPtr<TextTrackManager> mTextTrackManager;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ MOCHITEST_CHROME_MANIFESTS += [
|
|||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDateTimeInputArea.idl',
|
||||
'nsIFormSubmitObserver.idl',
|
||||
'nsIHTMLMenu.idl',
|
||||
'nsIImageDocument.idl',
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(465c0cc3-24cb-48ce-af1a-b18402326b05)]
|
||||
interface nsIDateTimeInputArea : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called from DOM/Layout when input element value has changed.
|
||||
*/
|
||||
void notifyInputElementValueChanged();
|
||||
|
||||
/**
|
||||
* Called when date/time picker value has changed.
|
||||
*/
|
||||
void setValueFromPicker(in jsval value);
|
||||
|
||||
/**
|
||||
* Called from DOM/Layout to set focus on inner text box.
|
||||
*/
|
||||
void focusInnerTextBox();
|
||||
|
||||
/**
|
||||
* Called from DOM/Layout to blur inner text box.
|
||||
*/
|
||||
void blurInnerTextBox();
|
||||
|
||||
/**
|
||||
* Set the current state of the picker, true if it's opened, false otherwise.
|
||||
*/
|
||||
void setPickerState(in boolean isOpen);
|
||||
};
|
|
@ -266,8 +266,11 @@ nsIFormControl::IsSingleLineTextControl(bool aExcludePassword, uint32_t aType)
|
|||
aType == NS_FORM_INPUT_TEL ||
|
||||
aType == NS_FORM_INPUT_URL ||
|
||||
// TODO: those are temporary until bug 773205 is fixed.
|
||||
aType == NS_FORM_INPUT_DATE ||
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
// On Android/B2G, date/time input appears as a normal text box.
|
||||
aType == NS_FORM_INPUT_TIME ||
|
||||
#endif
|
||||
aType == NS_FORM_INPUT_DATE ||
|
||||
aType == NS_FORM_INPUT_MONTH ||
|
||||
aType == NS_FORM_INPUT_WEEK ||
|
||||
(!aExcludePassword && aType == NS_FORM_INPUT_PASSWORD);
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<!-- In this case we're using reftest-wait to make sure the test doesn't
|
||||
get snapshotted before it's been focused. We're not testing
|
||||
invalidation so we don't need to listen for MozReftestInvalidate.
|
||||
-->
|
||||
<head>
|
||||
<script>
|
||||
function focusHandler() {
|
||||
setTimeout(function() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
}, 0);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="document.getElementById('t').focus();">
|
||||
<input type="time" id="t" onfocus="focusHandler();"
|
||||
style="-moz-appearance: none;">
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<!-- In this case we're using reftest-wait to make sure the test doesn't
|
||||
get snapshotted before it's been focused. We're not testing
|
||||
invalidation so we don't need to listen for MozReftestInvalidate.
|
||||
-->
|
||||
<head>
|
||||
<script>
|
||||
function focusHandler() {
|
||||
setTimeout(function() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
}, 0);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<input type="time" autofocus onfocus="focusHandler();"
|
||||
style="-moz-appearance: none;">
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
default-preferences pref(dom.forms.number,true)
|
||||
default-preferences pref(dom.forms.number,true) pref(dom.forms.datetime,true)
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == input-load.html input-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == input-create.html input-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == input-number.html input-number-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == input-time.html input-time-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == button-load.html button-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == button-create.html button-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == textarea-load.html textarea-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<div><img src=pass.png></div>
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<div></div>
|
||||
<script>
|
||||
var d = (new DOMParser()).parseFromString("<img src=pass.png>", "text/html");
|
||||
var n = d.adoptNode(d.querySelector('img'));
|
||||
document.querySelector('div').appendChild(n);
|
||||
</script>
|
||||
</html>
|
|
@ -35,6 +35,7 @@ skip-if(Android||B2G) == 649134-2.html 649134-2-ref.html
|
|||
== bug502168-1_malformed.html bug502168-1_well-formed.html
|
||||
|
||||
== responsive-image-load-shortcircuit.html responsive-image-load-shortcircuit-ref.html
|
||||
== image-load-shortcircuit.html image-load-shortcircuit-ref.html
|
||||
|
||||
# Test that image documents taken into account CSS properties like
|
||||
# image-orientation when determining the size of the image.
|
||||
|
|
|
@ -35,6 +35,10 @@ skip-if = buildapp == 'mulet'
|
|||
skip-if = android_version == '18' # Android, bug 1147974
|
||||
[test_input_color_picker_update.html]
|
||||
skip-if = android_version == '18' # Android, bug 1147974
|
||||
[test_input_datetime_focus_blur.html]
|
||||
skip-if = os == "android" || appname == "b2g"
|
||||
[test_input_datetime_tabindex.html]
|
||||
skip-if = os == "android" || appname == "b2g"
|
||||
[test_input_defaultValue.html]
|
||||
[test_input_email.html]
|
||||
[test_input_event.html]
|
||||
|
@ -64,6 +68,8 @@ skip-if = (toolkit == 'gonk' && debug) #debug-only failure; bug 926546
|
|||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
|
||||
[test_input_sanitization.html]
|
||||
[test_input_textarea_set_value_no_scroll.html]
|
||||
[test_input_time_key_events.html]
|
||||
skip-if = os == "android" || appname == "b2g"
|
||||
[test_input_types_pref.html]
|
||||
[test_input_typing_sanitization.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
|
||||
-->
|
||||
<head>
|
||||
<title>Test focus/blur behaviour for <input type='time'></title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<input id="input" type="time">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/**
|
||||
* Test for Bug 1288591.
|
||||
* This test checks whether date/time input types' .focus()/.blur() works
|
||||
* correctly. This test also checks when focusing on an date/time input element,
|
||||
* the focus is redirected to the anonymous text control, but the
|
||||
* document.activeElement still returns date/time input element.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function() {
|
||||
test();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
function test() {
|
||||
let time = document.getElementById("input");
|
||||
time.focus();
|
||||
|
||||
// The active element returns the input type=time.
|
||||
let activeElement = document.activeElement;
|
||||
is(activeElement, time, "activeElement should be the time element");
|
||||
is(activeElement.localName, "input", "activeElement should be an input element");
|
||||
is(activeElement.type, "time", "activeElement should be of type time");
|
||||
|
||||
// Use FocusManager to check that the actual focus is on the anonymous
|
||||
// text control.
|
||||
let fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIFocusManager);
|
||||
let focusedElement = fm.focusedElement;
|
||||
is(focusedElement.localName, "input", "focusedElement should be an input element");
|
||||
is(focusedElement.type, "text", "focusedElement should be of type text");
|
||||
|
||||
time.blur();
|
||||
isnot(document.activeElement, time, "activeElement should no longer be the time element");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,72 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
|
||||
-->
|
||||
<head>
|
||||
<title>Test tabindex attribute for <input type='time'></title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<input id="time1" type="time" tabindex="0">
|
||||
<input id="time2" type="time" tabindex="-1">
|
||||
<input id="time3" type="time" tabindex="0">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/**
|
||||
* Test for Bug 1288591.
|
||||
* This test checks whether date/time input types' tabindex attribute works
|
||||
* correctly.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function() {
|
||||
test();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
function test() {
|
||||
let time1 = document.getElementById("time1");
|
||||
let time2 = document.getElementById("time2");
|
||||
let time3 = document.getElementById("time3");
|
||||
|
||||
time1.focus();
|
||||
is(document.activeElement, time1,
|
||||
"input element with tabindex=0 is focusable");
|
||||
|
||||
// Advance to time1 minute field
|
||||
synthesizeKey("VK_TAB", {});
|
||||
is(document.activeElement, time1,
|
||||
"input element with tabindex=0 is tabbable");
|
||||
|
||||
// Advance to time1 AM/PM field
|
||||
synthesizeKey("VK_TAB", {});
|
||||
is(document.activeElement, time1,
|
||||
"input element with tabindex=0 is tabbable");
|
||||
|
||||
// Advance to next element
|
||||
synthesizeKey("VK_TAB", {});
|
||||
is(document.activeElement, time3,
|
||||
"input element with tabindex=-1 is not tabbable");
|
||||
|
||||
time2.focus();
|
||||
is(document.activeElement, time2,
|
||||
"input element with tabindex=-1 is still focusable");
|
||||
|
||||
// Changing the tabindex attribute dynamically.
|
||||
time3.setAttribute("tabindex", "-1");
|
||||
synthesizeKey("VK_TAB", {}); // need only one TAB since time2 is not tabbable
|
||||
isnot(document.activeElement, time3,
|
||||
"element with tabindex changed to -1 should not be tabbable");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,197 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
|
||||
-->
|
||||
<head>
|
||||
<title>Test key events for time control</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<input id="input" type="time">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// Turn off Spatial Navigation because it hijacks arrow keydown events:
|
||||
SimpleTest.waitForFocus(function() {
|
||||
SpecialPowers.pushPrefEnv({"set":[["snav.enabled", false]]}, function() {
|
||||
test();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
var testData = [
|
||||
/**
|
||||
* keys: keys to send to the input element.
|
||||
* initialVal: initial value set to the input element.
|
||||
* expectedVal: expected value of the input element after sending the keys.
|
||||
*/
|
||||
{
|
||||
// Type 1030 and select AM.
|
||||
keys: ["1030", "VK_DOWN"],
|
||||
initialVal: "",
|
||||
expectedVal: "10:30"
|
||||
},
|
||||
{
|
||||
// Type 3 in the hour field will automatically advance to the minute field.
|
||||
keys: ["330", "VK_DOWN"],
|
||||
initialVal: "",
|
||||
expectedVal: "03:30"
|
||||
},
|
||||
{
|
||||
// Type 5 in the hour field will automatically advance to the minute field.
|
||||
// Type 7 in the minute field will automatically advance to the AM/PM field.
|
||||
keys: ["57", "VK_DOWN"],
|
||||
initialVal: "",
|
||||
expectedVal: "05:07"
|
||||
},
|
||||
{
|
||||
// Advance to AM/PM field and change to PM.
|
||||
keys: ["VK_TAB", "VK_TAB", "VK_DOWN"],
|
||||
initialVal: "10:30",
|
||||
expectedVal: "22:30"
|
||||
},
|
||||
{
|
||||
// Right key should do the same thing as TAB key.
|
||||
keys: ["VK_RIGHT", "VK_RIGHT", "VK_DOWN"],
|
||||
initialVal: "10:30",
|
||||
expectedVal: "22:30"
|
||||
},
|
||||
{
|
||||
// Advance to minute field then back to hour field and decrement.
|
||||
keys: ["VK_RIGHT", "VK_LEFT", "VK_DOWN"],
|
||||
initialVal: "10:30",
|
||||
expectedVal: "09:30"
|
||||
},
|
||||
{
|
||||
// Focus starts on the first field, hour in this case, and increment.
|
||||
keys: ["VK_UP"],
|
||||
initialVal: "16:00",
|
||||
expectedVal: "17:00"
|
||||
},
|
||||
{
|
||||
// Advance to minute field and decrement.
|
||||
keys: ["VK_TAB", "VK_DOWN"],
|
||||
initialVal: "16:00",
|
||||
expectedVal: "16:59"
|
||||
},
|
||||
{
|
||||
// Advance to minute field and increment.
|
||||
keys: ["VK_TAB", "VK_UP"],
|
||||
initialVal: "16:59",
|
||||
expectedVal: "16:00"
|
||||
},
|
||||
{
|
||||
// PageUp on hour field increments hour by 3.
|
||||
keys: ["VK_PAGE_UP"],
|
||||
initialVal: "05:00",
|
||||
expectedVal: "08:00"
|
||||
},
|
||||
{
|
||||
// PageDown on hour field decrements hour by 3.
|
||||
keys: ["VK_PAGE_DOWN"],
|
||||
initialVal: "05:00",
|
||||
expectedVal: "02:00"
|
||||
},
|
||||
{
|
||||
// PageUp on minute field increments minute by 10.
|
||||
keys: ["VK_TAB", "VK_PAGE_UP"],
|
||||
initialVal: "14:00",
|
||||
expectedVal: "14:10"
|
||||
},
|
||||
{
|
||||
// PageDown on minute field decrements minute by 10.
|
||||
keys: ["VK_TAB", "VK_PAGE_DOWN"],
|
||||
initialVal: "14:00",
|
||||
expectedVal: "14:50"
|
||||
},
|
||||
{
|
||||
// Home key on hour field sets it to the minimum hour, which is 1 in 12-hour
|
||||
// clock.
|
||||
keys: ["VK_HOME"],
|
||||
initialVal: "03:10",
|
||||
expectedVal: "01:10"
|
||||
},
|
||||
{
|
||||
// End key on hour field sets it to the maximum hour, which is 12 in 12-hour
|
||||
// clock.
|
||||
keys: ["VK_END"],
|
||||
initialVal: "03:10",
|
||||
expectedVal: "00:10"
|
||||
},
|
||||
{
|
||||
// Home key on minute field sets it to the minimum minute, which is 0.
|
||||
keys: ["VK_TAB", "VK_HOME"],
|
||||
initialVal: "19:30",
|
||||
expectedVal: "19:00"
|
||||
},
|
||||
{
|
||||
// End key on minute field sets it to the minimum minute, which is 59.
|
||||
keys: ["VK_TAB", "VK_END"],
|
||||
initialVal: "19:30",
|
||||
expectedVal: "19:59"
|
||||
},
|
||||
// Second field will show up when needed.
|
||||
{
|
||||
// PageUp on second field increments second by 10.
|
||||
keys: ["VK_TAB", "VK_TAB", "VK_PAGE_UP"],
|
||||
initialVal: "08:10:10",
|
||||
expectedVal: "08:10:20"
|
||||
},
|
||||
{
|
||||
// PageDown on second field increments second by 10.
|
||||
keys: ["VK_TAB", "VK_TAB", "VK_PAGE_DOWN"],
|
||||
initialVal: "08:10:10",
|
||||
expectedVal: "08:10:00"
|
||||
},
|
||||
{
|
||||
// Home key on second field sets it to the minimum second, which is 0.
|
||||
keys: ["VK_TAB", "VK_TAB", "VK_HOME"],
|
||||
initialVal: "16:00:30",
|
||||
expectedVal: "16:00:00"
|
||||
},
|
||||
{
|
||||
// End key on second field sets it to the minimum second, which is 59.
|
||||
keys: ["VK_TAB", "VK_TAB", "VK_END"],
|
||||
initialVal: "16:00:30",
|
||||
expectedVal: "16:00:59"
|
||||
},
|
||||
];
|
||||
|
||||
function sendKeys(aKeys) {
|
||||
for (let i = 0; i < aKeys.length; i++) {
|
||||
let key = aKeys[i];
|
||||
if (key.startsWith("VK")) {
|
||||
synthesizeKey(key, {});
|
||||
} else {
|
||||
sendString(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function test() {
|
||||
var elem = document.getElementById("input");
|
||||
|
||||
for (let { keys, initialVal, expectedVal } of testData) {
|
||||
elem.focus();
|
||||
elem.value = initialVal;
|
||||
sendKeys(keys);
|
||||
elem.blur();
|
||||
is(elem.value, expectedVal,
|
||||
"Test with " + keys + ", result should be " + expectedVal);
|
||||
elem.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -160,27 +160,6 @@ function runTest()
|
|||
'1000-12-99',
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'time',
|
||||
validData: [
|
||||
'00:00',
|
||||
'09:09:00',
|
||||
'08:23:23.1',
|
||||
'21:43:56.12',
|
||||
'23:12:45.100',
|
||||
],
|
||||
invalidData: [
|
||||
'00:',
|
||||
'00:00:',
|
||||
'25:00',
|
||||
'-00:00',
|
||||
'00:00:00.',
|
||||
'00:60',
|
||||
'10:58:99',
|
||||
':19:10',
|
||||
'23:08:09.1012',
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'month',
|
||||
validData: [
|
||||
|
|
|
@ -247,8 +247,50 @@ TargetPrincipalDoesNotMatch=Failed to execute ‘postMessage’ on ‘DOMWindow
|
|||
RewriteYouTubeEmbed=Rewriting old-style YouTube Flash embed (%S) to iframe embed (%S). Please update page to use iframe instead of embed/object, if possible.
|
||||
# LOCALIZATION NOTE: Do not translate 'YouTube'. %S values are origins, like https://domain.com:port
|
||||
RewriteYouTubeEmbedPathParams=Rewriting old-style YouTube Flash embed (%S) to iframe embed (%S). Params were unsupported by iframe embeds and converted. Please update page to use iframe instead of embed/object, if possible.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker". %1$S is the ServiceWorker scope URL. %2$S is an error string.
|
||||
PushMessageDecryptionFailure=The ServiceWorker for scope ‘%1$S’ encountered an error decrypting a push message: ‘%2$S’. For help with encryption, please see https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Using_the_Push_API#Encryption
|
||||
# LOCALIZATION NOTE: This error is reported when the "Encryption" header for an
|
||||
# incoming push message is missing or invalid. Do not translate "ServiceWorker",
|
||||
# "Encryption", and "salt". %1$S is the ServiceWorker scope URL.
|
||||
PushMessageBadEncryptionHeader=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. The ‘Encryption’ header must include a unique ‘salt‘ parameter for each message.
|
||||
# LOCALIZATION NOTE: This error is reported when the "Crypto-Key" header for an
|
||||
# incoming push message is missing or invalid. Do not translate "ServiceWorker",
|
||||
# "Crypto-Key", and "dh". %1$S is the ServiceWorker scope URL.
|
||||
PushMessageBadCryptoKeyHeader=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. The ‘Crypto-Key‘ header must include a ‘dh‘ parameter containing the app server’s public key.
|
||||
# LOCALIZATION NOTE: This error is reported when a push message fails to decrypt because the deprecated
|
||||
# "Encryption-Key" header for an incoming push message is missing or invalid.
|
||||
# Do not translate "ServiceWorker", "Encryption-Key", "dh", "Crypto-Key", and
|
||||
# "Content-Encoding: aesgcm". %1$S is the ServiceWorker scope URL.
|
||||
PushMessageBadEncryptionKeyHeader=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. The ‘Encryption-Key’ header must include a ‘dh‘ parameter. This header is deprecated and will soon be removed. Please use ‘Crypto-Key‘ with ‘Content-Encoding: aesgcm‘ instead.
|
||||
# LOCALIZATION NOTE: This error is reported when a push message fails to decrypt
|
||||
# because the "Content-Encoding" header is missing or contains an
|
||||
# unsupported encoding. Do not translate "ServiceWorker", "Content-Encoding",
|
||||
# "aesgcm", and "aesgcm128". %1$S is the ServiceWorker scope URL.
|
||||
PushMessageBadEncodingHeader=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. The ‘Content-Encoding‘ header must be ‘aesgcm‘. ‘aesgcm128‘ is allowed, but deprecated and will soon be removed.
|
||||
# LOCALIZATION NOTE: This error is reported when a push message fails to decrypt
|
||||
# because the "dh" parameter is not valid base64url. Do not translate
|
||||
# "ServiceWorker", "dh", "Crypto-Key", and "base64url". %1$S is the
|
||||
# ServiceWorker scope URL.
|
||||
PushMessageBadSenderKey=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. The ‘dh‘ parameter in the ‘Crypto-Key‘ header must be the app server’s Diffie-Hellman public key, base64url-encoded (RFC 7515, Appendix C) and in “uncompressed” or “raw” form (65 bytes before encoding).
|
||||
# LOCALIZATION NOTE: This error is reported when a push message fails to decrypt
|
||||
# because the "salt" parameter is not valid base64url. Do not translate
|
||||
# "ServiceWorker", "salt", "Encryption", and "base64url". %1$S is the
|
||||
# ServiceWorker scope URL.
|
||||
PushMessageBadSalt=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. The ‘salt‘ parameter in the ‘Encryption‘ header must be base64url-encoded (RFC 7515, Appendix C), and be at least 16 bytes before encoding.
|
||||
# LOCALIZATION NOTE: This error is reported when a push message fails to decrypt
|
||||
# because the "rs" parameter is not a number, or is less than the pad size.
|
||||
# Do not translate "ServiceWorker", "rs", or "Encryption". %1$S is the
|
||||
# ServiceWorker scope URL. %2$S is the minimum value (1 for aesgcm128, 2 for
|
||||
# aesgcm).
|
||||
PushMessageBadRecordSize=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. The ‘rs‘ parameter of the ‘Encryption‘ header must be between %2$S and 2^36-31, or omitted entirely.
|
||||
# LOCALIZATION NOTE: This error is reported when a push message fails to decrypt
|
||||
# because an encrypted record is shorter than the pad size, the pad is larger
|
||||
# than the record, or any of the padding bytes are non-zero. Do not translate
|
||||
# "ServiceWorker". %1$S is the ServiceWorker scope URL. %2$S is the pad size
|
||||
# (1 for aesgcm128, 2 for aesgcm).
|
||||
PushMessageBadPaddingError=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. Each record in the encrypted message must be padded with a %2$S byte big-endian unsigned integer, followed by that number of zero-valued bytes.
|
||||
# LOCALIZATION NOTE: This error is reported when push message decryption fails
|
||||
# and no specific error info is available. Do not translate "ServiceWorker".
|
||||
# %1$S is the ServiceWorker scope URL.
|
||||
PushMessageBadCryptoError=The ServiceWorker for scope ‘%1$S’ failed to decrypt a push message. For help with encryption, please see https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Using_the_Push_API#Encryption
|
||||
# LOCALIZATION NOTE: %1$S is the type of a DOM event. 'passive' is a literal parameter from the DOM spec.
|
||||
PreventDefaultFromPassiveListenerWarning=Ignoring ‘preventDefault()’ call on event of type ‘%1$S’ from a listener registered as ‘passive’.
|
||||
FileLastModifiedDateWarning=File.lastModifiedDate is deprecated. Use File.lastModified instead.
|
||||
|
|
|
@ -34,6 +34,33 @@ struct DecryptResult {
|
|||
RefPtr<MediaRawData> mSample;
|
||||
};
|
||||
|
||||
class CDMKeyInfo {
|
||||
public:
|
||||
explicit CDMKeyInfo(const nsTArray<uint8_t>& aKeyId)
|
||||
: mKeyId(aKeyId)
|
||||
, mStatus()
|
||||
{}
|
||||
|
||||
CDMKeyInfo(const nsTArray<uint8_t>& aKeyId,
|
||||
const dom::Optional<dom::MediaKeyStatus>& aStatus)
|
||||
: mKeyId(aKeyId)
|
||||
, mStatus(aStatus.Value())
|
||||
{}
|
||||
|
||||
// The copy-ctor and copy-assignment operator for Optional<T> are declared as
|
||||
// delete, so override CDMKeyInfo copy-ctor for nsTArray operations.
|
||||
CDMKeyInfo(const CDMKeyInfo& aKeyInfo)
|
||||
{
|
||||
mKeyId = aKeyInfo.mKeyId;
|
||||
if (aKeyInfo.mStatus.WasPassed()) {
|
||||
mStatus.Construct(aKeyInfo.mStatus.Value());
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> mKeyId;
|
||||
dom::Optional<dom::MediaKeyStatus> mStatus;
|
||||
};
|
||||
|
||||
typedef int64_t UnixTime;
|
||||
|
||||
// Proxies calls CDM, and proxies calls back.
|
||||
|
|
|
@ -41,16 +41,12 @@ public:
|
|||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage) = 0;
|
||||
|
||||
virtual void KeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId,
|
||||
mozilla::dom::MediaKeyStatus aStatus) = 0;
|
||||
|
||||
virtual void ForgetKeyStatus(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) = 0;
|
||||
|
||||
virtual void Decrypted(uint32_t aId,
|
||||
mozilla::DecryptStatus aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData) = 0;
|
||||
|
||||
virtual void BatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<mozilla::CDMKeyInfo>& aKeyInfos) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -280,39 +280,26 @@ GMPCDMCallbackProxy::SessionError(const nsCString& aSessionId,
|
|||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::KeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId,
|
||||
dom::MediaKeyStatus aStatus)
|
||||
GMPCDMCallbackProxy::BatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
KeyStatusChangedInternal(aSessionId,
|
||||
aKeyId,
|
||||
dom::Optional<dom::MediaKeyStatus>(aStatus));
|
||||
BatchedKeyStatusChangedInternal(aSessionId, aKeyInfos);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::ForgetKeyStatus(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
KeyStatusChangedInternal(aSessionId,
|
||||
aKeyId,
|
||||
dom::Optional<dom::MediaKeyStatus>());
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::KeyStatusChangedInternal(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId,
|
||||
const dom::Optional<dom::MediaKeyStatus>& aStatus)
|
||||
GMPCDMCallbackProxy::BatchedKeyStatusChangedInternal(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos)
|
||||
{
|
||||
bool keyStatusesChange = false;
|
||||
{
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
keyStatusesChange = caps.SetKeyStatus(aKeyId,
|
||||
NS_ConvertUTF8toUTF16(aSessionId),
|
||||
aStatus);
|
||||
for (size_t i = 0; i < aKeyInfos.Length(); i++) {
|
||||
keyStatusesChange |=
|
||||
caps.SetKeyStatus(aKeyInfos[i].mKeyId,
|
||||
NS_ConvertUTF8toUTF16(aSessionId),
|
||||
aKeyInfos[i].mStatus);
|
||||
}
|
||||
}
|
||||
if (keyStatusesChange) {
|
||||
nsCOMPtr<nsIRunnable> task;
|
||||
|
|
|
@ -43,17 +43,13 @@ public:
|
|||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage) override;
|
||||
|
||||
void KeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId,
|
||||
dom::MediaKeyStatus aStatus) override;
|
||||
|
||||
void ForgetKeyStatus(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) override;
|
||||
|
||||
void Decrypted(uint32_t aId,
|
||||
DecryptStatus aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData) override;
|
||||
|
||||
void BatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos) override;
|
||||
|
||||
void Terminated() override;
|
||||
|
||||
~GMPCDMCallbackProxy() {}
|
||||
|
@ -61,10 +57,9 @@ public:
|
|||
private:
|
||||
friend class GMPCDMProxy;
|
||||
explicit GMPCDMCallbackProxy(CDMProxy* aProxy);
|
||||
void KeyStatusChangedInternal(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId,
|
||||
const dom::Optional<dom::MediaKeyStatus>& aStatus);
|
||||
|
||||
void BatchedKeyStatusChangedInternal(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos);
|
||||
// Warning: Weak ref.
|
||||
CDMProxy* mProxy;
|
||||
};
|
||||
|
|
|
@ -161,16 +161,29 @@ GMPDecryptorChild::KeyStatusChanged(const char* aSessionId,
|
|||
{
|
||||
AutoTArray<uint8_t, 16> kid;
|
||||
kid.AppendElements(aKeyId, aKeyIdLength);
|
||||
if (aStatus == kGMPUnknown) {
|
||||
CALL_ON_GMP_THREAD(SendForgetKeyStatus,
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
kid);
|
||||
} else {
|
||||
CALL_ON_GMP_THREAD(SendKeyStatusChanged,
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
kid,
|
||||
aStatus);
|
||||
|
||||
nsTArray<GMPKeyInformation> keyInfos;
|
||||
keyInfos.AppendElement(GMPKeyInformation(kid, aStatus));
|
||||
CALL_ON_GMP_THREAD(SendBatchedKeyStatusChanged,
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
keyInfos);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::BatchedKeyStatusChanged(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const GMPMediaKeyInfo* aKeyInfos,
|
||||
uint32_t aKeyInfosLength)
|
||||
{
|
||||
nsTArray<GMPKeyInformation> keyInfos;
|
||||
for (uint32_t i = 0; i < aKeyInfosLength; i++) {
|
||||
nsTArray<uint8_t> keyId;
|
||||
keyId.AppendElements(aKeyInfos[i].keyid, aKeyInfos[i].keyid_size);
|
||||
keyInfos.AppendElement(GMPKeyInformation(keyId, aKeyInfos[i].status));
|
||||
}
|
||||
CALL_ON_GMP_THREAD(SendBatchedKeyStatusChanged,
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
keyInfos);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -73,6 +73,11 @@ public:
|
|||
|
||||
void Decrypted(GMPBuffer* aBuffer, GMPErr aResult) override;
|
||||
|
||||
void BatchedKeyStatusChanged(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const GMPMediaKeyInfo* aKeyInfos,
|
||||
uint32_t aKeyInfosLength) override;
|
||||
|
||||
// GMPDecryptorHost
|
||||
void GetSandboxVoucher(const uint8_t** aVoucher,
|
||||
uint32_t* aVoucherLength) override;
|
||||
|
|
|
@ -358,28 +358,27 @@ ToMediaKeyStatus(GMPMediaKeyStatus aStatus) {
|
|||
}
|
||||
|
||||
bool
|
||||
GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId,
|
||||
InfallibleTArray<uint8_t>&& aKeyId,
|
||||
const GMPMediaKeyStatus& aStatus)
|
||||
GMPDecryptorParent::RecvBatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
InfallibleTArray<GMPKeyInformation>&& aKeyInfos)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvKeyStatusChanged(sessionId='%s', keyId=%s, status=%d)",
|
||||
this, aSessionId.get(), ToBase64(aKeyId).get(), aStatus));
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvBatchedKeyStatusChanged(sessionId='%s', KeyInfos len='%d')",
|
||||
this, aSessionId.get(), aKeyInfos.Length()));
|
||||
|
||||
if (mIsOpen) {
|
||||
mCallback->KeyStatusChanged(aSessionId, aKeyId, ToMediaKeyStatus(aStatus));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPDecryptorParent::RecvForgetKeyStatus(const nsCString& aSessionId,
|
||||
InfallibleTArray<uint8_t>&& aKeyId)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvForgetKeyStatus(sessionId='%s', keyId=%s)",
|
||||
this, aSessionId.get(), ToBase64(aKeyId).get()));
|
||||
|
||||
if (mIsOpen) {
|
||||
mCallback->ForgetKeyStatus(aSessionId, aKeyId);
|
||||
nsTArray<CDMKeyInfo> cdmKeyInfos(aKeyInfos.Length());
|
||||
for (uint32_t i = 0; i < aKeyInfos.Length(); i++) {
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvBatchedKeyStatusChanged(keyId=%s, gmp-status=%d)",
|
||||
this, ToBase64(aKeyInfos[i].keyId()).get(), aKeyInfos[i].status()));
|
||||
// If the status is kGMPUnknown, we're going to forget(remove) that key info.
|
||||
if (aKeyInfos[i].status() != kGMPUnknown) {
|
||||
auto status = ToMediaKeyStatus(aKeyInfos[i].status());
|
||||
cdmKeyInfos.AppendElement(CDMKeyInfo(aKeyInfos[i].keyId(),
|
||||
dom::Optional<dom::MediaKeyStatus>(status)));
|
||||
} else {
|
||||
cdmKeyInfos.AppendElement(CDMKeyInfo(aKeyInfos[i].keyId()));
|
||||
}
|
||||
}
|
||||
mCallback->BatchedKeyStatusChanged(aSessionId, cdmKeyInfos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -98,17 +98,13 @@ private:
|
|||
const uint32_t& aSystemCode,
|
||||
const nsCString& aMessage) override;
|
||||
|
||||
bool RecvKeyStatusChanged(const nsCString& aSessionId,
|
||||
InfallibleTArray<uint8_t>&& aKeyId,
|
||||
const GMPMediaKeyStatus& aStatus) override;
|
||||
|
||||
bool RecvForgetKeyStatus(const nsCString& aSessionId,
|
||||
InfallibleTArray<uint8_t>&& aKeyId) override;
|
||||
|
||||
bool RecvDecrypted(const uint32_t& aId,
|
||||
const GMPErr& aErr,
|
||||
InfallibleTArray<uint8_t>&& aBuffer) override;
|
||||
|
||||
bool RecvBatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
InfallibleTArray<GMPKeyInformation>&& aKeyInfos) override;
|
||||
|
||||
bool RecvShutdown() override;
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
using GMPBufferType from "gmp-video-codec.h";
|
||||
using GMPAudioCodecType from "gmp-audio-codec.h";
|
||||
using GMPMediaKeyStatus from "gmp-decryption.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
@ -76,5 +77,10 @@ struct GMPAudioDecodedSampleData
|
|||
uint32_t mSamplesPerSecond;
|
||||
};
|
||||
|
||||
struct GMPKeyInformation {
|
||||
uint8_t[] keyId;
|
||||
GMPMediaKeyStatus status;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ include protocol PGMPContent;
|
|||
include GMPTypes;
|
||||
|
||||
using GMPSessionMessageType from "gmp-decryption.h";
|
||||
using GMPMediaKeyStatus from "gmp-decryption.h";
|
||||
using GMPSessionType from "gmp-decryption.h";
|
||||
using GMPDOMException from "gmp-decryption.h";
|
||||
using GMPErr from "gmp-errors.h";
|
||||
|
@ -79,14 +78,12 @@ parent:
|
|||
uint32_t aSystemCode,
|
||||
nsCString aMessage);
|
||||
|
||||
async KeyStatusChanged(nsCString aSessionId, uint8_t[] aKey,
|
||||
GMPMediaKeyStatus aStatus);
|
||||
|
||||
async ForgetKeyStatus(nsCString aSessionId, uint8_t[] aKey);
|
||||
|
||||
async Decrypted(uint32_t aId, GMPErr aResult, uint8_t[] aBuffer);
|
||||
|
||||
async Shutdown();
|
||||
|
||||
async BatchedKeyStatusChanged(nsCString aSessionId,
|
||||
GMPKeyInformation[] aKeyInfos);
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -102,6 +102,20 @@ enum GMPMediaKeyStatus {
|
|||
kGMPMediaKeyStatusInvalid = 8 // Must always be last.
|
||||
};
|
||||
|
||||
struct GMPMediaKeyInfo {
|
||||
GMPMediaKeyInfo() {}
|
||||
GMPMediaKeyInfo(const uint8_t* aKeyId,
|
||||
uint32_t aKeyIdSize,
|
||||
GMPMediaKeyStatus aStatus)
|
||||
: keyid(aKeyId)
|
||||
, keyid_size(aKeyIdSize)
|
||||
, status(aStatus)
|
||||
{}
|
||||
const uint8_t* keyid;
|
||||
uint32_t keyid_size;
|
||||
GMPMediaKeyStatus status;
|
||||
};
|
||||
|
||||
// Time in milliseconds, as offset from epoch, 1 Jan 1970.
|
||||
typedef int64_t GMPTimestamp;
|
||||
|
||||
|
@ -192,6 +206,12 @@ public:
|
|||
// Returns decrypted buffer to Gecko, or reports failure.
|
||||
virtual void Decrypted(GMPBuffer* aBuffer, GMPErr aResult) = 0;
|
||||
|
||||
// To aggregate KeyStatusChanged into single callback per session id.
|
||||
virtual void BatchedKeyStatusChanged(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const GMPMediaKeyInfo* aKeyInfos,
|
||||
uint32_t aKeyInfosLength) = 0;
|
||||
|
||||
virtual ~GMPDecryptorCallback() {}
|
||||
};
|
||||
|
||||
|
|
|
@ -409,13 +409,15 @@ WidevineDecryptor::OnSessionKeysChange(const char* aSessionId,
|
|||
return;
|
||||
}
|
||||
Log("Decryptor::OnSessionKeysChange()");
|
||||
|
||||
nsTArray<GMPMediaKeyInfo> key_infos;
|
||||
for (uint32_t i = 0; i < aKeysInfoCount; i++) {
|
||||
mCallback->KeyStatusChanged(aSessionId,
|
||||
aSessionIdSize,
|
||||
aKeysInfo[i].key_id,
|
||||
aKeysInfo[i].key_id_size,
|
||||
ToGMPKeyStatus(aKeysInfo[i].status));
|
||||
key_infos.AppendElement(GMPMediaKeyInfo(aKeysInfo[i].key_id,
|
||||
aKeysInfo[i].key_id_size,
|
||||
ToGMPKeyStatus(aKeysInfo[i].status)));
|
||||
}
|
||||
mCallback->BatchedKeyStatusChanged(aSessionId, aSessionIdSize,
|
||||
key_infos.Elements(), key_infos.Length());
|
||||
}
|
||||
|
||||
static GMPTimestamp
|
||||
|
|
|
@ -1388,16 +1388,13 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
|||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage) override {}
|
||||
void KeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId,
|
||||
mozilla::dom::MediaKeyStatus aStatus) override { }
|
||||
|
||||
void ForgetKeyStatus(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) override { }
|
||||
|
||||
void Decrypted(uint32_t aId,
|
||||
mozilla::DecryptStatus aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData) override { }
|
||||
|
||||
void BatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos) override { }
|
||||
|
||||
void Terminated() override {
|
||||
if (mDecryptor) {
|
||||
mDecryptor->Close();
|
||||
|
|
|
@ -1758,6 +1758,8 @@ TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
|
|||
// Remove all coded frames from track buffer that have a presentation timestamp greater than or equal to presentation timestamp and less than frame end timestamp.
|
||||
// If highest end timestamp for track buffer is set and less than or equal to presentation timestamp:
|
||||
// Remove all coded frames from track buffer that have a presentation timestamp greater than or equal to highest end timestamp and less than frame end timestamp"
|
||||
TimeUnit intervalsEnd = aIntervals.GetEnd();
|
||||
bool mayBreakLoop = false;
|
||||
for (uint32_t i = aStartIndex; i < data.Length(); i++) {
|
||||
const RefPtr<MediaRawData> sample = data[i];
|
||||
TimeInterval sampleInterval =
|
||||
|
@ -1768,6 +1770,14 @@ TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
|
|||
firstRemovedIndex = Some(i);
|
||||
}
|
||||
lastRemovedIndex = i;
|
||||
mayBreakLoop = false;
|
||||
continue;
|
||||
}
|
||||
if (sample->mKeyframe && mayBreakLoop) {
|
||||
break;
|
||||
}
|
||||
if (sampleInterval.mStart > intervalsEnd) {
|
||||
mayBreakLoop = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -298,6 +298,10 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
|
|||
LOG(("Audio engine is not initalized"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
// Until we fix (or wallpaper) support for multiple mic input
|
||||
// (Bug 1238038) fail allocation for a second device
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!AllocChannel()) {
|
||||
LOG(("Audio device is not initalized"));
|
||||
|
|
|
@ -7,10 +7,15 @@
|
|||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, 'gDOMBundle', () =>
|
||||
Services.strings.createBundle('chrome://global/locale/dom/dom.properties'));
|
||||
|
||||
Cu.importGlobalProperties(['crypto']);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['PushCrypto', 'concatArray',
|
||||
'getCryptoParams'];
|
||||
this.EXPORTED_SYMBOLS = ['PushCrypto', 'concatArray'];
|
||||
|
||||
var UTF8 = new TextEncoder('utf-8');
|
||||
|
||||
|
@ -30,6 +35,59 @@ var ECDSA_KEY = { name: 'ECDSA', namedCurve: 'P-256' };
|
|||
// A default keyid with a name that won't conflict with a real keyid.
|
||||
var DEFAULT_KEYID = '';
|
||||
|
||||
/** Localized error property names. */
|
||||
|
||||
// `Encryption` header missing or malformed.
|
||||
const BAD_ENCRYPTION_HEADER = 'PushMessageBadEncryptionHeader';
|
||||
// `Crypto-Key` or legacy `Encryption-Key` header missing.
|
||||
const BAD_CRYPTO_KEY_HEADER = 'PushMessageBadCryptoKeyHeader';
|
||||
const BAD_ENCRYPTION_KEY_HEADER = 'PushMessageBadEncryptionKeyHeader';
|
||||
// `Content-Encoding` header missing or contains unsupported encoding.
|
||||
const BAD_ENCODING_HEADER = 'PushMessageBadEncodingHeader';
|
||||
// `dh` parameter of `Crypto-Key` header missing or not base64url-encoded.
|
||||
const BAD_DH_PARAM = 'PushMessageBadSenderKey';
|
||||
// `salt` parameter of `Encryption` header missing or not base64url-encoded.
|
||||
const BAD_SALT_PARAM = 'PushMessageBadSalt';
|
||||
// `rs` parameter of `Encryption` header not a number or less than pad size.
|
||||
const BAD_RS_PARAM = 'PushMessageBadRecordSize';
|
||||
// Invalid or insufficient padding for encrypted chunk.
|
||||
const BAD_PADDING = 'PushMessageBadPaddingError';
|
||||
// Generic crypto error.
|
||||
const BAD_CRYPTO = 'PushMessageBadCryptoError';
|
||||
|
||||
class CryptoError extends Error {
|
||||
/**
|
||||
* Creates an error object indicating an incoming push message could not be
|
||||
* decrypted.
|
||||
*
|
||||
* @param {String} message A human-readable error message. This is only for
|
||||
* internal module logging, and doesn't need to be localized.
|
||||
* @param {String} property The localized property name from `dom.properties`.
|
||||
* @param {String...} params Substitutions to insert into the localized
|
||||
* string.
|
||||
*/
|
||||
constructor(message, property, ...params) {
|
||||
super(message);
|
||||
this.isCryptoError = true;
|
||||
this.property = property;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a localized string for reporting decryption errors to the Web
|
||||
* Console.
|
||||
*
|
||||
* @param {String} scope The scope of the service worker receiving the
|
||||
* message, prepended to any other substitutions in the string.
|
||||
* @returns {String} The localized string.
|
||||
*/
|
||||
format(scope) {
|
||||
let params = [scope, ...this.params].map(String);
|
||||
return gDOMBundle.formatStringFromName(this.property, params,
|
||||
params.length);
|
||||
}
|
||||
}
|
||||
|
||||
function getEncryptionKeyParams(encryptKeyField) {
|
||||
if (!encryptKeyField) {
|
||||
return null;
|
||||
|
@ -48,50 +106,84 @@ function getEncryptionKeyParams(encryptKeyField) {
|
|||
}
|
||||
|
||||
function getEncryptionParams(encryptField) {
|
||||
if (!encryptField) {
|
||||
throw new CryptoError('Missing encryption header',
|
||||
BAD_ENCRYPTION_HEADER);
|
||||
}
|
||||
var p = encryptField.split(',', 1)[0];
|
||||
if (!p) {
|
||||
return null;
|
||||
throw new CryptoError('Encryption header missing params',
|
||||
BAD_ENCRYPTION_HEADER);
|
||||
}
|
||||
return p.split(';').reduce(parseHeaderFieldParams, {});
|
||||
}
|
||||
|
||||
this.getCryptoParams = function(headers) {
|
||||
function getCryptoParams(headers) {
|
||||
if (!headers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var keymap;
|
||||
var padSize;
|
||||
if (!headers.encoding) {
|
||||
throw new CryptoError('Missing Content-Encoding header',
|
||||
BAD_ENCODING_HEADER);
|
||||
}
|
||||
if (headers.encoding == AESGCM_ENCODING) {
|
||||
// aesgcm uses the Crypto-Key header, 2 bytes for the pad length, and an
|
||||
// authentication secret.
|
||||
// https://tools.ietf.org/html/draft-ietf-httpbis-encryption-encoding-01
|
||||
keymap = getEncryptionKeyParams(headers.crypto_key);
|
||||
if (!keymap) {
|
||||
throw new CryptoError('Missing Crypto-Key header',
|
||||
BAD_CRYPTO_KEY_HEADER);
|
||||
}
|
||||
padSize = 2;
|
||||
} else if (headers.encoding == AESGCM128_ENCODING) {
|
||||
// aesgcm128 uses Encryption-Key, 1 byte for the pad length, and no secret.
|
||||
// https://tools.ietf.org/html/draft-thomson-http-encryption-02
|
||||
keymap = getEncryptionKeyParams(headers.encryption_key);
|
||||
if (!keymap) {
|
||||
throw new CryptoError('Missing Encryption-Key header',
|
||||
BAD_ENCRYPTION_KEY_HEADER);
|
||||
}
|
||||
padSize = 1;
|
||||
}
|
||||
if (!keymap) {
|
||||
return null;
|
||||
} else {
|
||||
throw new CryptoError('Unsupported Content-Encoding: ' + headers.encoding,
|
||||
BAD_ENCODING_HEADER);
|
||||
}
|
||||
|
||||
var enc = getEncryptionParams(headers.encryption);
|
||||
if (!enc) {
|
||||
return null;
|
||||
}
|
||||
var dh = keymap[enc.keyid || DEFAULT_KEYID];
|
||||
if (!dh) {
|
||||
throw new CryptoError('Missing dh parameter', BAD_DH_PARAM);
|
||||
}
|
||||
var salt = enc.salt;
|
||||
var rs = (enc.rs)? parseInt(enc.rs, 10) : 4096;
|
||||
|
||||
if (!dh || !salt || isNaN(rs) || (rs <= padSize)) {
|
||||
return null;
|
||||
if (!salt) {
|
||||
throw new CryptoError('Missing salt parameter', BAD_SALT_PARAM);
|
||||
}
|
||||
var rs = enc.rs ? parseInt(enc.rs, 10) : 4096;
|
||||
if (isNaN(rs)) {
|
||||
throw new CryptoError('rs parameter must be a number', BAD_RS_PARAM);
|
||||
}
|
||||
if (rs <= padSize) {
|
||||
throw new CryptoError('rs parameter must be at least ' + padSize,
|
||||
BAD_RS_PARAM, padSize);
|
||||
}
|
||||
return {dh, salt, rs, padSize};
|
||||
}
|
||||
|
||||
// Decodes an unpadded, base64url-encoded string.
|
||||
function base64URLDecode(string) {
|
||||
try {
|
||||
return ChromeUtils.base64URLDecode(string, {
|
||||
// draft-ietf-httpbis-encryption-encoding-01 prohibits padding.
|
||||
padding: 'reject',
|
||||
});
|
||||
} catch (ex) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
var parseHeaderFieldParams = (m, v) => {
|
||||
var i = v.indexOf('=');
|
||||
if (i >= 0) {
|
||||
|
@ -150,7 +242,7 @@ hkdf.prototype.extract = function(info, len) {
|
|||
.then(prkh => prkh.hash(input))
|
||||
.then(h => {
|
||||
if (h.byteLength < len) {
|
||||
throw new Error('Length is too long');
|
||||
throw new CryptoError('HKDF length is too long', BAD_CRYPTO);
|
||||
}
|
||||
return h.slice(0, len);
|
||||
});
|
||||
|
@ -159,7 +251,7 @@ hkdf.prototype.extract = function(info, len) {
|
|||
/* generate a 96-bit nonce for use in GCM, 48-bits of which are populated */
|
||||
function generateNonce(base, index) {
|
||||
if (index >= Math.pow(2, 48)) {
|
||||
throw new Error('Error generating nonce - index is too large.');
|
||||
throw new CryptoError('Nonce index is too large', BAD_CRYPTO);
|
||||
}
|
||||
var nonce = base.slice(0, 12);
|
||||
nonce = new Uint8Array(nonce);
|
||||
|
@ -190,24 +282,66 @@ this.PushCrypto = {
|
|||
]));
|
||||
},
|
||||
|
||||
decodeMsg(aData, aPrivateKey, aPublicKey, aSenderPublicKey, aSalt, aRs,
|
||||
aAuthenticationSecret, aPadSize) {
|
||||
/**
|
||||
* Decrypts a push message.
|
||||
*
|
||||
* @param {JsonWebKey} privateKey The ECDH private key of the subscription
|
||||
* receiving the message, in JWK form.
|
||||
* @param {BufferSource} publicKey The ECDH public key of the subscription
|
||||
* receiving the message, in raw form.
|
||||
* @param {BufferSource} authenticationSecret The 16-byte shared
|
||||
* authentication secret of the subscription receiving the message.
|
||||
* @param {Object} headers The encryption headers passed to `getCryptoParams`.
|
||||
* @param {BufferSource} ciphertext The encrypted message data.
|
||||
* @returns {Promise} Resolves with a `Uint8Array` containing the decrypted
|
||||
* message data. Rejects with a `CryptoError` if decryption fails.
|
||||
*/
|
||||
decrypt(privateKey, publicKey, authenticationSecret, headers, ciphertext) {
|
||||
return Promise.resolve().then(_ => {
|
||||
let cryptoParams = getCryptoParams(headers);
|
||||
if (!cryptoParams) {
|
||||
return null;
|
||||
}
|
||||
return this._decodeMsg(ciphertext, privateKey, publicKey,
|
||||
cryptoParams.dh, cryptoParams.salt,
|
||||
cryptoParams.rs, authenticationSecret,
|
||||
cryptoParams.padSize);
|
||||
}).catch(error => {
|
||||
if (error.isCryptoError) {
|
||||
throw error;
|
||||
}
|
||||
// Web Crypto returns an unhelpful "operation failed for an
|
||||
// operation-specific reason" error if decryption fails. We don't have
|
||||
// context about what went wrong, so we throw a generic error instead.
|
||||
throw new CryptoError('Bad encryption', BAD_CRYPTO);
|
||||
});
|
||||
},
|
||||
|
||||
_decodeMsg(aData, aPrivateKey, aPublicKey, aSenderPublicKey, aSalt, aRs,
|
||||
aAuthenticationSecret, aPadSize) {
|
||||
|
||||
if (aData.byteLength === 0) {
|
||||
// Zero length messages will be passed as null.
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
// The last chunk of data must be less than aRs, if it is not return an
|
||||
// error.
|
||||
if (aData.byteLength % (aRs + 16) === 0) {
|
||||
return Promise.reject(new Error('Data truncated'));
|
||||
throw new CryptoError('Encrypted data truncated', BAD_CRYPTO);
|
||||
}
|
||||
|
||||
let senderKey = ChromeUtils.base64URLDecode(aSenderPublicKey, {
|
||||
// draft-ietf-httpbis-encryption-encoding-01 prohibits padding.
|
||||
padding: "reject",
|
||||
});
|
||||
let senderKey = base64URLDecode(aSenderPublicKey);
|
||||
if (!senderKey) {
|
||||
throw new CryptoError('dh parameter is not base64url-encoded',
|
||||
BAD_DH_PARAM);
|
||||
}
|
||||
|
||||
let salt = base64URLDecode(aSalt);
|
||||
if (!salt) {
|
||||
throw new CryptoError('salt parameter is not base64url-encoded',
|
||||
BAD_SALT_PARAM);
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
crypto.subtle.importKey('raw', senderKey, ECDH_KEY,
|
||||
|
@ -220,8 +354,7 @@ this.PushCrypto = {
|
|||
subscriptionPrivateKey, 256))
|
||||
.then(ikm => this._deriveKeyAndNonce(aPadSize,
|
||||
new Uint8Array(ikm),
|
||||
ChromeUtils.base64URLDecode(aSalt,
|
||||
{ padding: "reject" }),
|
||||
salt,
|
||||
aPublicKey,
|
||||
senderKey,
|
||||
aAuthenticationSecret))
|
||||
|
@ -298,22 +431,23 @@ this.PushCrypto = {
|
|||
*/
|
||||
_unpadChunk(padSize, decoded) {
|
||||
if (padSize < 1 || padSize > 2) {
|
||||
throw new Error('Unsupported pad size');
|
||||
throw new CryptoError('Unsupported pad size', BAD_CRYPTO);
|
||||
}
|
||||
if (decoded.length < padSize) {
|
||||
throw new Error('Decoded array is too short!');
|
||||
throw new CryptoError('Decoded array is too short!', BAD_PADDING,
|
||||
padSize);
|
||||
}
|
||||
var pad = decoded[0];
|
||||
if (padSize == 2) {
|
||||
pad = (pad << 8) | decoded[1];
|
||||
}
|
||||
if (pad > decoded.length) {
|
||||
throw new Error ('Padding is wrong!');
|
||||
throw new CryptoError('Padding is wrong!', BAD_PADDING, padSize);
|
||||
}
|
||||
// All padded bytes must be zero except the first one.
|
||||
for (var i = padSize; i <= pad; i++) {
|
||||
if (decoded[i] !== 0) {
|
||||
throw new Error('Padding is wrong!');
|
||||
throw new CryptoError('Padding is wrong!', BAD_PADDING, padSize);
|
||||
}
|
||||
}
|
||||
return decoded.slice(pad + padSize);
|
||||
|
|
|
@ -12,12 +12,15 @@ const Cr = Components.results;
|
|||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const {PushCrypto} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||
const {
|
||||
PushCrypto,
|
||||
getCryptoParams,
|
||||
CryptoError,
|
||||
} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||
const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm");
|
||||
|
||||
const CONNECTION_PROTOCOLS = (function() {
|
||||
|
@ -35,9 +38,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "gPushNotifier",
|
|||
"@mozilla.org/push/Notifier;1",
|
||||
"nsIPushNotifier");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gDOMBundle", () =>
|
||||
Services.strings.createBundle("chrome://global/locale/dom/dom.properties"));
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PushService"];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
|
@ -752,8 +752,8 @@ this.PushService = {
|
|||
* @param {String} messageID The message ID, used to report service worker
|
||||
* delivery failures. For Web Push messages, this is the version. If empty,
|
||||
* failures will not be reported.
|
||||
* @param {Object} headers The encryption headers.
|
||||
* @param {ArrayBuffer|Uint8Array} data The encrypted message data.
|
||||
* @param {Object} cryptoParams The message encryption settings.
|
||||
* @param {Function} updateFunc A function that receives the existing
|
||||
* registration record as its argument, and returns a new record. If the
|
||||
* function returns `null` or `undefined`, the record will not be updated.
|
||||
|
@ -762,14 +762,11 @@ this.PushService = {
|
|||
* @returns {Promise} Resolves with an `nsIPushErrorReporter` ack status
|
||||
* code, indicating whether the message was delivered successfully.
|
||||
*/
|
||||
receivedPushMessage(keyID, messageID, data, cryptoParams, updateFunc) {
|
||||
receivedPushMessage(keyID, messageID, headers, data, updateFunc) {
|
||||
console.debug("receivedPushMessage()");
|
||||
Services.telemetry.getHistogramById("PUSH_API_NOTIFICATION_RECEIVED").add();
|
||||
|
||||
return this._updateRecordAfterPush(keyID, updateFunc).then(record => {
|
||||
if (!record) {
|
||||
throw new Error("Ignoring update for key ID " + keyID);
|
||||
}
|
||||
if (record.quotaApplies()) {
|
||||
// Update quota after the delay, at which point
|
||||
// we check for visible notifications.
|
||||
|
@ -782,7 +779,7 @@ this.PushService = {
|
|||
}, prefs.get("quotaUpdateDelay"));
|
||||
this._updateQuotaTimeouts.add(timeoutID);
|
||||
}
|
||||
return this._decryptAndNotifyApp(record, messageID, data, cryptoParams);
|
||||
return this._decryptAndNotifyApp(record, messageID, headers, data);
|
||||
}).catch(error => {
|
||||
console.error("receivedPushMessage: Error notifying app", error);
|
||||
return Ci.nsIPushErrorReporter.ACK_NOT_DELIVERED;
|
||||
|
@ -794,7 +791,7 @@ this.PushService = {
|
|||
*
|
||||
* @param {String} keyID The push registration ID.
|
||||
* @param {Function} updateFunc The function passed to `receivedPushMessage`.
|
||||
* @returns {Promise} Resolves with the updated record, or `null` if the
|
||||
* @returns {Promise} Resolves with the updated record, or rejects if the
|
||||
* record was not updated.
|
||||
*/
|
||||
_updateRecordAfterPush(keyID, updateFunc) {
|
||||
|
@ -832,54 +829,34 @@ this.PushService = {
|
|||
});
|
||||
});
|
||||
}).then(record => {
|
||||
if (record) {
|
||||
gPushNotifier.notifySubscriptionModified(record.scope,
|
||||
record.principal);
|
||||
if (!record) {
|
||||
throw new Error("Ignoring update for key ID " + keyID);
|
||||
}
|
||||
gPushNotifier.notifySubscriptionModified(record.scope,
|
||||
record.principal);
|
||||
return record;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Decrypts a message. Will resolve with null if cryptoParams is falsy.
|
||||
*
|
||||
* @param {PushRecord} record The receiving registration.
|
||||
* @param {ArrayBuffer|Uint8Array} data The encrypted message data.
|
||||
* @param {Object} cryptoParams The message encryption settings.
|
||||
* @returns {Promise} Resolves with the decrypted message.
|
||||
*/
|
||||
_decryptMessage(data, record, cryptoParams) {
|
||||
if (!cryptoParams) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return PushCrypto.decodeMsg(
|
||||
data,
|
||||
record.p256dhPrivateKey,
|
||||
record.p256dhPublicKey,
|
||||
cryptoParams.dh,
|
||||
cryptoParams.salt,
|
||||
cryptoParams.rs,
|
||||
record.authenticationSecret,
|
||||
cryptoParams.padSize
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Decrypts an incoming message and notifies the associated service worker.
|
||||
*
|
||||
* @param {PushRecord} record The receiving registration.
|
||||
* @param {String} messageID The message ID.
|
||||
* @param {Object} headers The encryption headers.
|
||||
* @param {ArrayBuffer|Uint8Array} data The encrypted message data.
|
||||
* @param {Object} cryptoParams The message encryption settings.
|
||||
* @returns {Promise} Resolves with an ack status code.
|
||||
*/
|
||||
_decryptAndNotifyApp(record, messageID, data, cryptoParams) {
|
||||
return this._decryptMessage(data, record, cryptoParams)
|
||||
_decryptAndNotifyApp(record, messageID, headers, data) {
|
||||
return PushCrypto.decrypt(record.p256dhPrivateKey, record.p256dhPublicKey,
|
||||
record.authenticationSecret, headers, data)
|
||||
.then(
|
||||
message => this._notifyApp(record, messageID, message),
|
||||
error => {
|
||||
let message = gDOMBundle.formatStringFromName(
|
||||
"PushMessageDecryptionFailure", [record.scope, String(error)], 2);
|
||||
console.warn("decryptAndNotifyApp: Error decrypting message",
|
||||
record.scope, messageID, error);
|
||||
|
||||
let message = error.format(record.scope);
|
||||
gPushNotifier.notifyError(record.scope, record.principal, message,
|
||||
Ci.nsIScriptError.errorFlag);
|
||||
return Ci.nsIPushErrorReporter.ACK_DECRYPTION_ERROR;
|
||||
|
|
|
@ -12,10 +12,7 @@ const Cr = Components.results;
|
|||
|
||||
const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm");
|
||||
const {PushRecord} = Cu.import("resource://gre/modules/PushRecord.jsm");
|
||||
const {
|
||||
PushCrypto,
|
||||
getCryptoParams,
|
||||
} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||
const {PushCrypto} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||
Cu.import("resource://gre/modules/Messaging.jsm"); /*global: Messaging */
|
||||
Cu.import("resource://gre/modules/Services.jsm"); /*global: Services */
|
||||
Cu.import("resource://gre/modules/Preferences.jsm"); /*global: Preferences */
|
||||
|
@ -107,36 +104,35 @@ this.PushServiceAndroidGCM = {
|
|||
data = JSON.parse(data);
|
||||
console.debug("ReceivedPushMessage with data", data);
|
||||
|
||||
let { message, cryptoParams } = this._messageAndCryptoParams(data);
|
||||
let { headers, message } = this._messageAndHeaders(data);
|
||||
|
||||
console.debug("Delivering message to main PushService:", message, cryptoParams);
|
||||
console.debug("Delivering message to main PushService:", message, headers);
|
||||
this._mainPushService.receivedPushMessage(
|
||||
data.channelID, "", message, cryptoParams, (record) => {
|
||||
data.channelID, "", headers, message, (record) => {
|
||||
// Always update the stored record.
|
||||
return record;
|
||||
});
|
||||
},
|
||||
|
||||
_messageAndCryptoParams(data) {
|
||||
_messageAndHeaders(data) {
|
||||
// Default is no data (and no encryption).
|
||||
let message = null;
|
||||
let cryptoParams = null;
|
||||
let headers = null;
|
||||
|
||||
if (data.message && data.enc && (data.enckey || data.cryptokey)) {
|
||||
let headers = {
|
||||
headers = {
|
||||
encryption_key: data.enckey,
|
||||
crypto_key: data.cryptokey,
|
||||
encryption: data.enc,
|
||||
encoding: data.con,
|
||||
};
|
||||
cryptoParams = getCryptoParams(headers);
|
||||
// Ciphertext is (urlsafe) Base 64 encoded.
|
||||
message = ChromeUtils.base64URLDecode(data.message, {
|
||||
// The Push server may append padding.
|
||||
padding: "ignore",
|
||||
});
|
||||
}
|
||||
return { message, cryptoParams };
|
||||
return { headers, message };
|
||||
},
|
||||
|
||||
_configure: function(serverURL, debug) {
|
||||
|
|
|
@ -23,7 +23,6 @@ Cu.import("resource://gre/modules/Promise.jsm");
|
|||
const {
|
||||
PushCrypto,
|
||||
concatArray,
|
||||
getCryptoParams,
|
||||
} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PushServiceHttp2"];
|
||||
|
@ -157,13 +156,12 @@ PushChannelListener.prototype = {
|
|||
encryption: getHeaderField(aRequest, "Encryption"),
|
||||
encoding: getHeaderField(aRequest, "Content-Encoding"),
|
||||
};
|
||||
let cryptoParams = getCryptoParams(headers);
|
||||
let msg = concatArray(this._message);
|
||||
|
||||
this._mainListener._pushService._pushChannelOnStop(this._mainListener.uri,
|
||||
this._ackUri,
|
||||
msg,
|
||||
cryptoParams);
|
||||
headers,
|
||||
msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -784,11 +782,11 @@ this.PushServiceHttp2 = {
|
|||
}
|
||||
},
|
||||
|
||||
_pushChannelOnStop: function(aUri, aAckUri, aMessage, cryptoParams) {
|
||||
_pushChannelOnStop: function(aUri, aAckUri, aHeaders, aMessage) {
|
||||
console.debug("pushChannelOnStop()");
|
||||
|
||||
this._mainPushService.receivedPushMessage(
|
||||
aUri, "", aMessage, cryptoParams, record => {
|
||||
aUri, "", aHeaders, aMessage, record => {
|
||||
// Always update the stored record.
|
||||
return record;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
|
||||
const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm");
|
||||
const {PushRecord} = Cu.import("resource://gre/modules/PushRecord.jsm");
|
||||
const {
|
||||
PushCrypto,
|
||||
getCryptoParams,
|
||||
} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||
const {PushCrypto} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||
|
||||
const kPUSHWSDB_DB_NAME = "pushapi";
|
||||
const kPUSHWSDB_DB_VERSION = 5; // Change this if the IndexedDB format changes
|
||||
|
@ -689,22 +686,17 @@ this.PushServiceWebSocket = {
|
|||
updateRecord
|
||||
);
|
||||
} else {
|
||||
let params = getCryptoParams(update.headers);
|
||||
if (params) {
|
||||
let message = ChromeUtils.base64URLDecode(update.data, {
|
||||
// The Push server may append padding.
|
||||
padding: "ignore",
|
||||
});
|
||||
promise = this._mainPushService.receivedPushMessage(
|
||||
update.channelID,
|
||||
update.version,
|
||||
message,
|
||||
params,
|
||||
updateRecord
|
||||
);
|
||||
} else {
|
||||
promise = Promise.reject(new Error("Invalid crypto headers"));
|
||||
}
|
||||
let message = ChromeUtils.base64URLDecode(update.data, {
|
||||
// The Push server may append padding.
|
||||
padding: "ignore",
|
||||
});
|
||||
promise = this._mainPushService.receivedPushMessage(
|
||||
update.channelID,
|
||||
update.version,
|
||||
update.headers,
|
||||
message,
|
||||
updateRecord
|
||||
);
|
||||
}
|
||||
promise.then(status => {
|
||||
this._sendAck(update.channelID, update.version, status);
|
||||
|
|
|
@ -10,9 +10,8 @@ function run_test() {
|
|||
}
|
||||
|
||||
add_task(function* test_crypto_getCryptoParams() {
|
||||
let testData = [
|
||||
// These headers should parse correctly.
|
||||
{
|
||||
let shouldParse = [{
|
||||
desc: 'aesgcm with multiple keys',
|
||||
headers: {
|
||||
encoding: 'aesgcm',
|
||||
|
@ -77,24 +76,25 @@ add_task(function* test_crypto_getCryptoParams() {
|
|||
rs: 4096,
|
||||
padSize: 1,
|
||||
}
|
||||
},
|
||||
}];
|
||||
for (let test of shouldParse) {
|
||||
let params = getCryptoParams(test.headers);
|
||||
deepEqual(params, test.params, test.desc);
|
||||
}
|
||||
|
||||
// These headers should be rejected.
|
||||
{
|
||||
let shouldThrow = [{
|
||||
desc: 'aesgcm128 with Crypto-Key',
|
||||
headers: {
|
||||
encoding: 'aesgcm128',
|
||||
crypto_key: 'keyid=v2; dh=VA6wmY1IpiE',
|
||||
encryption: 'keyid=v2; salt=F0Im7RtGgNY',
|
||||
},
|
||||
params: null,
|
||||
},
|
||||
{
|
||||
}, {
|
||||
desc: 'Invalid encoding',
|
||||
headers: {
|
||||
encoding: 'nonexistent',
|
||||
},
|
||||
params: null,
|
||||
}, {
|
||||
desc: 'Invalid record size',
|
||||
headers: {
|
||||
|
@ -102,7 +102,6 @@ add_task(function* test_crypto_getCryptoParams() {
|
|||
crypto_key: 'dh=pbmv1QkcEDY',
|
||||
encryption: 'dh=Esao8aTBfIk;rs=bad',
|
||||
},
|
||||
params: null,
|
||||
}, {
|
||||
desc: 'Insufficiently large record size',
|
||||
headers: {
|
||||
|
@ -110,7 +109,6 @@ add_task(function* test_crypto_getCryptoParams() {
|
|||
crypto_key: 'dh=fK0EXaw5IU8',
|
||||
encryption: 'salt=orbLLmlbJfM;rs=1',
|
||||
},
|
||||
params: null,
|
||||
}, {
|
||||
desc: 'aesgcm with Encryption-Key',
|
||||
headers: {
|
||||
|
@ -118,12 +116,9 @@ add_task(function* test_crypto_getCryptoParams() {
|
|||
encryption_key: 'dh=FplK5KkvUF0',
|
||||
encryption: 'salt=p6YHhFF3BQY',
|
||||
},
|
||||
params: null,
|
||||
}];
|
||||
|
||||
for (let test of testData) {
|
||||
let params = getCryptoParams(test.headers);
|
||||
deepEqual(params, test.params, test.desc);
|
||||
for (let test of shouldThrow) {
|
||||
throws(() => getCryptoParams(test.headers), test.desc);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -145,37 +140,41 @@ add_task(function* test_crypto_decodeMsg() {
|
|||
desc: 'padSize = 2, rs = 24, pad = 0',
|
||||
result: 'Some message',
|
||||
data: 'Oo34w2F9VVnTMFfKtdx48AZWQ9Li9M6DauWJVgXU',
|
||||
senderPublicKey: 'BCHFVrflyxibGLlgztLwKelsRZp4gqX3tNfAKFaxAcBhpvYeN1yIUMrxaDKiLh4LNKPtj0BOXGdr-IQ-QP82Wjo',
|
||||
salt: 'zCU18Rw3A5aB_Xi-vfixmA',
|
||||
rs: 24,
|
||||
authSecret: 'aTDc6JebzR6eScy2oLo4RQ',
|
||||
padSize: 2,
|
||||
headers: {
|
||||
crypto_key: 'dh=BCHFVrflyxibGLlgztLwKelsRZp4gqX3tNfAKFaxAcBhpvYeN1yIUMrxaDKiLh4LNKPtj0BOXGdr-IQ-QP82Wjo',
|
||||
encryption: 'salt=zCU18Rw3A5aB_Xi-vfixmA; rs=24',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}, {
|
||||
desc: 'padSize = 2, rs = 8, pad = 16',
|
||||
result: 'Yet another message',
|
||||
data: 'uEC5B_tR-fuQ3delQcrzrDCp40W6ipMZjGZ78USDJ5sMj-6bAOVG3AK6JqFl9E6AoWiBYYvMZfwThVxmDnw6RHtVeLKFM5DWgl1EwkOohwH2EhiDD0gM3io-d79WKzOPZE9rDWUSv64JstImSfX_ADQfABrvbZkeaWxh53EG59QMOElFJqHue4dMURpsMXg',
|
||||
senderPublicKey: 'BEaA4gzA3i0JDuirGhiLgymS4hfFX7TNTdEhSk_HBlLpkjgCpjPL5c-GL9uBGIfa_fhGNKKFhXz1k9Kyens2ZpQ',
|
||||
salt: 'ZFhzj0S-n29g9P2p4-I7tA',
|
||||
rs: 8,
|
||||
authSecret: '6plwZnSpVUbF7APDXus3UQ',
|
||||
padSize: 2,
|
||||
headers: {
|
||||
crypto_key: 'dh=BEaA4gzA3i0JDuirGhiLgymS4hfFX7TNTdEhSk_HBlLpkjgCpjPL5c-GL9uBGIfa_fhGNKKFhXz1k9Kyens2ZpQ',
|
||||
encryption: 'salt=ZFhzj0S-n29g9P2p4-I7tA; rs=8',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}, {
|
||||
desc: 'padSize = 1, rs = 4096, pad = 2',
|
||||
result: 'aesgcm128 encrypted message',
|
||||
data: 'ljBJ44NPzJFH9EuyT5xWMU4vpZ90MdAqaq1TC1kOLRoPNHtNFXeJ0GtuSaE',
|
||||
senderPublicKey: 'BOmnfg02vNd6RZ7kXWWrCGFF92bI-rQ-bV0Pku3-KmlHwbGv4ejWqgasEdLGle5Rhmp6SKJunZw2l2HxKvrIjfI',
|
||||
salt: 'btxxUtclbmgcc30b9rT3Bg',
|
||||
rs: 4096,
|
||||
padSize: 1,
|
||||
headers: {
|
||||
encryption_key: 'dh=BOmnfg02vNd6RZ7kXWWrCGFF92bI-rQ-bV0Pku3-KmlHwbGv4ejWqgasEdLGle5Rhmp6SKJunZw2l2HxKvrIjfI',
|
||||
encryption: 'salt=btxxUtclbmgcc30b9rT3Bg; rs=4096',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
}, {
|
||||
desc: 'padSize = 2, rs = 3, pad = 0',
|
||||
result: 'Small record size',
|
||||
data: 'oY4e5eDatDVt2fpQylxbPJM-3vrfhDasfPc8Q1PWt4tPfMVbz_sDNL_cvr0DXXkdFzS1lxsJsj550USx4MMl01ihjImXCjrw9R5xFgFrCAqJD3GwXA1vzS4T5yvGVbUp3SndMDdT1OCcEofTn7VC6xZ-zP8rzSQfDCBBxmPU7OISzr8Z4HyzFCGJeBfqiZ7yUfNlKF1x5UaZ4X6iU_TXx5KlQy_toV1dXZ2eEAMHJUcSdArvB6zRpFdEIxdcHcJyo1BIYgAYTDdAIy__IJVCPY_b2CE5W_6ohlYKB7xDyH8giNuWWXAgBozUfScLUVjPC38yJTpAUi6w6pXgXUWffende5FreQpnMFL1L4G-38wsI_-ISIOzdO8QIrXHxmtc1S5xzYu8bMqSgCinvCEwdeGFCmighRjj8t1zRWo0D14rHbQLPR_b1P5SvEeJTtS9Nm3iibM',
|
||||
senderPublicKey: 'BCg6ZIGuE2ZNm2ti6Arf4CDVD_8--aLXAGLYhpghwjl1xxVjTLLpb7zihuEOGGbyt8Qj0_fYHBP4ObxwJNl56bk',
|
||||
salt: '5LIDBXbvkBvvb7ZdD-T4PQ',
|
||||
rs: 3,
|
||||
authSecret: 'g2rWVHUCpUxgcL9Tz7vyeQ',
|
||||
padSize: 2,
|
||||
headers: {
|
||||
crypto_key: 'dh=BCg6ZIGuE2ZNm2ti6Arf4CDVD_8--aLXAGLYhpghwjl1xxVjTLLpb7zihuEOGGbyt8Qj0_fYHBP4ObxwJNl56bk',
|
||||
encryption: 'salt=5LIDBXbvkBvvb7ZdD-T4PQ; rs=3',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}];
|
||||
for (let test of expectedSuccesses) {
|
||||
let authSecret = test.authSecret ? ChromeUtils.base64URLDecode(test.authSecret, {
|
||||
|
@ -184,10 +183,8 @@ add_task(function* test_crypto_decodeMsg() {
|
|||
let data = ChromeUtils.base64URLDecode(test.data, {
|
||||
padding: "reject",
|
||||
});
|
||||
let result = yield PushCrypto.decodeMsg(data,
|
||||
privateKey, publicKey,
|
||||
test.senderPublicKey, test.salt,
|
||||
test.rs, authSecret, test.padSize);
|
||||
let result = yield PushCrypto.decrypt(privateKey, publicKey, authSecret,
|
||||
test.headers, data);
|
||||
let decoder = new TextDecoder('utf-8');
|
||||
equal(decoder.decode(new Uint8Array(result)), test.result, test.desc);
|
||||
}
|
||||
|
@ -195,38 +192,46 @@ add_task(function* test_crypto_decodeMsg() {
|
|||
let expectedFailures = [{
|
||||
desc: 'padSize = 1, rs = 4096, auth secret, pad = 8',
|
||||
data: 'h0FmyldY8aT5EQ6CJrbfRn_IdDvytoLeHb9_q5CjtdFRfgDRknxLmOzavLaVG4oOiS0r',
|
||||
senderPublicKey: 'BCXHk7O8CE-9AOp6xx7g7c-NCaNpns1PyyHpdcmDaijLbT6IdGq0ezGatBwtFc34BBfscFxdk4Tjksa2Mx5rRCM',
|
||||
salt: 'aGBpoKklLtrLcAUCcCr7JQ',
|
||||
rs: 4096,
|
||||
senderPublicKey: '',
|
||||
authSecret: 'Sxb6u0gJIhGEogyLawjmCw',
|
||||
padSize: 1,
|
||||
headers: {
|
||||
crypto_key: 'dh=BCXHk7O8CE-9AOp6xx7g7c-NCaNpns1PyyHpdcmDaijLbT6IdGq0ezGatBwtFc34BBfscFxdk4Tjksa2Mx5rRCM',
|
||||
encryption: 'salt=aGBpoKklLtrLcAUCcCr7JQ',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
}, {
|
||||
desc: 'Missing padding',
|
||||
data: 'anvsHj7oBQTPMhv7XSJEsvyMS4-8EtbC7HgFZsKaTg',
|
||||
senderPublicKey: 'BMSqfc3ohqw2DDgu3nsMESagYGWubswQPGxrW1bAbYKD18dIHQBUmD3ul_lu7MyQiT5gNdzn5JTXQvCcpf-oZE4',
|
||||
salt: 'Czx2i18rar8XWOXAVDnUuw',
|
||||
rs: 4096,
|
||||
padSize: 1,
|
||||
headers: {
|
||||
crypto_key: 'dh=BMSqfc3ohqw2DDgu3nsMESagYGWubswQPGxrW1bAbYKD18dIHQBUmD3ul_lu7MyQiT5gNdzn5JTXQvCcpf-oZE4',
|
||||
encryption: 'salt=Czx2i18rar8XWOXAVDnUuw',
|
||||
encoding: 'aesgcm128',
|
||||
},
|
||||
}, {
|
||||
desc: 'padSize > rs',
|
||||
data: 'Ct_h1g7O55e6GvuhmpjLsGnv8Rmwvxgw8iDESNKGxk_8E99iHKDzdV8wJPyHA-6b2E6kzuVa5UWiQ7s4Zms1xzJ4FKgoxvBObXkc_r_d4mnb-j245z3AcvRmcYGk5_HZ0ci26SfhAN3lCgxGzTHS4nuHBRkGwOb4Tj4SFyBRlLoTh2jyVK2jYugNjH9tTrGOBg7lP5lajLTQlxOi91-RYZSfFhsLX3LrAkXuRoN7G1CdiI7Y3_eTgbPIPabDcLCnGzmFBTvoJSaQF17huMl_UnWoCj2WovA4BwK_TvWSbdgElNnQ4CbArJ1h9OqhDOphVu5GUGr94iitXRQR-fqKPMad0ULLjKQWZOnjuIdV1RYEZ873r62Yyd31HoveJcSDb1T8l_QK2zVF8V4k0xmK9hGuC0rF5YJPYPHgl5__usknzxMBnRrfV5_MOL5uPZwUEFsu',
|
||||
senderPublicKey: 'BAcMdWLJRGx-kPpeFtwqR3GE1LWzd1TYh2rg6CEFu53O-y3DNLkNe_BtGtKRR4f7ZqpBMVS6NgfE2NwNPm3Ndls',
|
||||
salt: 'NQVTKhB0rpL7ZzKkotTGlA',
|
||||
rs: 1,
|
||||
authSecret: '6plwZnSpVUbF7APDXus3UQ',
|
||||
padSize: 2,
|
||||
headers: {
|
||||
crypto_key: 'dh=BAcMdWLJRGx-kPpeFtwqR3GE1LWzd1TYh2rg6CEFu53O-y3DNLkNe_BtGtKRR4f7ZqpBMVS6NgfE2NwNPm3Ndls',
|
||||
encryption: 'salt=NQVTKhB0rpL7ZzKkotTGlA; rs=1',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}, {
|
||||
desc: 'Encrypted with padSize = 1, decrypted with padSize = 2 and auth secret',
|
||||
data: 'fwkuwTTChcLnrzsbDI78Y2EoQzfnbMI8Ax9Z27_rwX8',
|
||||
senderPublicKey: 'BCHn-I-J3dfPRLJBlNZ3xFoAqaBLZ6qdhpaz9W7Q00JW1oD-hTxyEECn6KYJNK8AxKUyIDwn6Icx_PYWJiEYjQ0',
|
||||
salt: 'c6JQl9eJ0VvwrUVCQDxY7Q',
|
||||
rs: 4096,
|
||||
authSecret: 'BhbpNTWyO5wVJmVKTV6XaA',
|
||||
padSize: 2,
|
||||
headers: {
|
||||
crypto_key: 'dh=BCHn-I-J3dfPRLJBlNZ3xFoAqaBLZ6qdhpaz9W7Q00JW1oD-hTxyEECn6KYJNK8AxKUyIDwn6Icx_PYWJiEYjQ0',
|
||||
encryption: 'salt=c6JQl9eJ0VvwrUVCQDxY7Q',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}, {
|
||||
desc: 'Truncated input',
|
||||
data: 'AlDjj6NvT5HGyrHbT8M5D6XBFSra6xrWS9B2ROaCIjwSu3RyZ1iyuv0',
|
||||
rs: 25,
|
||||
headers: {
|
||||
crypto_key: 'dh=BCHn-I-J3dfPRLJBlNZ3xFoAqaBLZ6qdhpaz9W7Q00JW1oD-hTxyEECn6KYJNK8AxKUyIDwn6Icx_PYWJiEYjQ0',
|
||||
encryption: 'salt=c6JQl9eJ0VvwrUVCQDxY7Q; rs=25',
|
||||
encoding: 'aesgcm',
|
||||
},
|
||||
}];
|
||||
for (let test of expectedFailures) {
|
||||
let authSecret = test.authSecret ? ChromeUtils.base64URLDecode(test.authSecret, {
|
||||
|
@ -236,9 +241,8 @@ add_task(function* test_crypto_decodeMsg() {
|
|||
padding: "reject",
|
||||
});
|
||||
yield rejects(
|
||||
PushCrypto.decodeMsg(data, privateKey, publicKey,
|
||||
test.senderPublicKey, test.salt, test.rs,
|
||||
authSecret, test.padSize),
|
||||
PushCrypto.decrypt(privateKey, publicKey, authSecret,
|
||||
test.headers, data),
|
||||
test.desc
|
||||
);
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ partial interface HTMLInputElement {
|
|||
// This is similar to set .value on nsIDOMInput/TextAreaElements, but handling
|
||||
// of the value change is closer to the normal user input, so 'change' event
|
||||
// for example will be dispatched when focusing out the element.
|
||||
[ChromeOnly]
|
||||
[Func="IsChromeOrXBL", NeedsSubjectPrincipal]
|
||||
void setUserInput(DOMString input);
|
||||
};
|
||||
|
||||
|
@ -234,3 +234,28 @@ partial interface HTMLInputElement {
|
|||
[Pref="dom.webkitBlink.dirPicker.enabled", BinaryName="WebkitDirectoryAttr", SetterThrows]
|
||||
attribute boolean webkitdirectory;
|
||||
};
|
||||
|
||||
dictionary DateTimeValue {
|
||||
long hour;
|
||||
long minute;
|
||||
};
|
||||
|
||||
partial interface HTMLInputElement {
|
||||
[Pref="dom.forms.datetime", ChromeOnly]
|
||||
DateTimeValue getDateTimeInputBoxValue();
|
||||
|
||||
[Pref="dom.forms.datetime", ChromeOnly]
|
||||
void updateDateTimeInputBox(optional DateTimeValue value);
|
||||
|
||||
[Pref="dom.forms.datetime", ChromeOnly]
|
||||
void setDateTimePickerState(boolean open);
|
||||
|
||||
[Pref="dom.forms.datetime", Func="IsChromeOrXBL"]
|
||||
void openDateTimePicker(optional DateTimeValue initialValue);
|
||||
|
||||
[Pref="dom.forms.datetime", Func="IsChromeOrXBL"]
|
||||
void updateDateTimePicker(optional DateTimeValue value);
|
||||
|
||||
[Pref="dom.forms.datetime", Func="IsChromeOrXBL"]
|
||||
void closeDateTimePicker();
|
||||
};
|
||||
|
|
|
@ -861,7 +861,8 @@ nsXULElement::BindToTree(nsIDocument* aDocument,
|
|||
// Note that add-ons may introduce bindings that cause this assertion to
|
||||
// fire.
|
||||
NS_ASSERTION(IsInVideoControls(this) ||
|
||||
IsInFeedSubscribeLine(this),
|
||||
IsInFeedSubscribeLine(this) ||
|
||||
IsXULElement(nsGkAtoms::datetimebox),
|
||||
"Unexpected XUL element in non-XUL doc");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ GPUProcessManager::GPUProcessManager()
|
|||
mProcess(nullptr),
|
||||
mGPUChild(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(GPUProcessManager);
|
||||
|
||||
mObserver = new Observer(this);
|
||||
nsContentUtils::RegisterShutdownObserver(mObserver);
|
||||
|
||||
|
@ -71,6 +73,8 @@ GPUProcessManager::GPUProcessManager()
|
|||
|
||||
GPUProcessManager::~GPUProcessManager()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GPUProcessManager);
|
||||
|
||||
LayerTreeOwnerTracker::Shutdown();
|
||||
|
||||
// The GPU process should have already been shut down.
|
||||
|
@ -372,14 +376,8 @@ GPUProcessManager::NotifyRemoteActorDestroyed(const uint64_t& aProcessToken)
|
|||
void
|
||||
GPUProcessManager::CleanShutdown()
|
||||
{
|
||||
if (!mProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NS_FREE_PERMANENT_DATA
|
||||
mVsyncBridge->Close();
|
||||
#endif
|
||||
DestroyProcess();
|
||||
mVsyncIOThread = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -11,15 +11,22 @@ namespace gfx {
|
|||
|
||||
VsyncIOThreadHolder::VsyncIOThreadHolder()
|
||||
{
|
||||
MOZ_COUNT_CTOR(VsyncIOThreadHolder);
|
||||
}
|
||||
|
||||
VsyncIOThreadHolder::~VsyncIOThreadHolder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(VsyncIOThreadHolder);
|
||||
|
||||
if (!mThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(NewRunnableMethod(mThread, &nsIThread::AsyncShutdown));
|
||||
if (NS_IsMainThread()) {
|
||||
mThread->AsyncShutdown();
|
||||
} else {
|
||||
NS_DispatchToMainThread(NewRunnableMethod(mThread, &nsIThread::AsyncShutdown));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -731,7 +731,10 @@ BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
|
|||
}
|
||||
|
||||
// We don't own it, apparently.
|
||||
mFirstSource = nullptr;
|
||||
if (mFirstSource) {
|
||||
mNeedsFullUpdate = true;
|
||||
mFirstSource = nullptr;
|
||||
}
|
||||
|
||||
DataTextureSource* texture = aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
|
||||
|
||||
|
|
|
@ -3654,8 +3654,12 @@ nsCSSFrameConstructor::FindInputData(Element* aElement,
|
|||
SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame),
|
||||
// TODO: this is temporary until a frame is written: bug 888320.
|
||||
SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame),
|
||||
// TODO: this is temporary until a frame is written: bug 888320
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
// On Android/B2G, date/time input appears as a normal text box.
|
||||
SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewTextControlFrame),
|
||||
#else
|
||||
SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewDateTimeControlFrame),
|
||||
#endif
|
||||
// TODO: this is temporary until a frame is written: bug 888320
|
||||
SIMPLE_INT_CREATE(NS_FORM_INPUT_MONTH, NS_NewTextControlFrame),
|
||||
// TODO: this is temporary until a frame is written: bug 888320
|
||||
|
|
|
@ -22,6 +22,7 @@ UNIFIED_SOURCES += [
|
|||
'nsButtonFrameRenderer.cpp',
|
||||
'nsColorControlFrame.cpp',
|
||||
'nsComboboxControlFrame.cpp',
|
||||
'nsDateTimeControlFrame.cpp',
|
||||
'nsFieldSetFrame.cpp',
|
||||
'nsFileControlFrame.cpp',
|
||||
'nsFormControlFrame.cpp',
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This frame type is used for input type=date, time, month, week, and
|
||||
* datetime-local.
|
||||
*/
|
||||
|
||||
#include "nsDateTimeControlFrame.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsFormControlFrame.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "nsContentList.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsIDateTimeInputArea.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
#include "nsIDOMMutationEvent.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
nsIFrame*
|
||||
NS_NewDateTimeControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
{
|
||||
return new (aPresShell) nsDateTimeControlFrame(aContext);
|
||||
}
|
||||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsDateTimeControlFrame)
|
||||
|
||||
NS_QUERYFRAME_HEAD(nsDateTimeControlFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsDateTimeControlFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
|
||||
|
||||
nsDateTimeControlFrame::nsDateTimeControlFrame(nsStyleContext* aContext)
|
||||
: nsContainerFrame(aContext)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
||||
{
|
||||
nsContentUtils::DestroyAnonymousContent(&mInputAreaContent);
|
||||
nsContainerFrame::DestroyFrom(aDestructRoot);
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::UpdateInputBoxValue()
|
||||
{
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
inputAreaContent->NotifyInputElementValueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::SetValueFromPicker(const DateTimeValue& aValue)
|
||||
{
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
AutoJSAPI api;
|
||||
if (!api.Init(mContent->OwnerDoc()->GetScopeObject())) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject* wrapper = mContent->GetWrapper();
|
||||
if (!wrapper) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject* scope = xpc::GetXBLScope(api.cx(), wrapper);
|
||||
AutoJSAPI jsapi;
|
||||
if (!scope || !jsapi.Init(scope)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> jsValue(jsapi.cx());
|
||||
if (!ToJSValue(jsapi.cx(), aValue, &jsValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputAreaContent->SetValueFromPicker(jsValue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::SetPickerState(bool aOpen)
|
||||
{
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
inputAreaContent->SetPickerState(aOpen);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::HandleFocusEvent()
|
||||
{
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
inputAreaContent->FocusInnerTextBox();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::HandleBlurEvent()
|
||||
{
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
inputAreaContent->BlurInnerTextBox();
|
||||
}
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsDateTimeControlFrame::GetMinISize(nsRenderingContext* aRenderingContext)
|
||||
{
|
||||
nscoord result;
|
||||
DISPLAY_MIN_WIDTH(this, result);
|
||||
|
||||
nsIFrame* kid = mFrames.FirstChild();
|
||||
if (kid) { // display:none?
|
||||
result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
|
||||
kid,
|
||||
nsLayoutUtils::MIN_ISIZE);
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsDateTimeControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
|
||||
{
|
||||
nscoord result;
|
||||
DISPLAY_PREF_WIDTH(this, result);
|
||||
|
||||
nsIFrame* kid = mFrames.FirstChild();
|
||||
if (kid) { // display:none?
|
||||
result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
|
||||
kid,
|
||||
nsLayoutUtils::PREF_ISIZE);
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
MarkInReflow();
|
||||
|
||||
DO_GLOBAL_REFLOW_COUNT("nsDateTimeControlFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
|
||||
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
||||
("enter nsDateTimeControlFrame::Reflow: availSize=%d,%d",
|
||||
aReflowInput.AvailableWidth(),
|
||||
aReflowInput.AvailableHeight()));
|
||||
|
||||
NS_ASSERTION(mInputAreaContent, "The input area content must exist!");
|
||||
|
||||
const WritingMode myWM = aReflowInput.GetWritingMode();
|
||||
|
||||
// The ISize of our content box, which is the available ISize
|
||||
// for our anonymous content:
|
||||
const nscoord contentBoxISize = aReflowInput.ComputedISize();
|
||||
nscoord contentBoxBSize = aReflowInput.ComputedBSize();
|
||||
|
||||
// Figure out our border-box sizes as well (by adding borderPadding to
|
||||
// content-box sizes):
|
||||
const nscoord borderBoxISize = contentBoxISize +
|
||||
aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM);
|
||||
|
||||
nscoord borderBoxBSize;
|
||||
if (contentBoxBSize != NS_INTRINSICSIZE) {
|
||||
borderBoxBSize = contentBoxBSize +
|
||||
aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
|
||||
} // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize.
|
||||
|
||||
nsIFrame* inputAreaFrame = mFrames.FirstChild();
|
||||
if (!inputAreaFrame) { // display:none?
|
||||
if (contentBoxBSize == NS_INTRINSICSIZE) {
|
||||
contentBoxBSize = 0;
|
||||
borderBoxBSize =
|
||||
aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
|
||||
}
|
||||
} else {
|
||||
NS_ASSERTION(inputAreaFrame->GetContent() == mInputAreaContent,
|
||||
"What is this child doing here?");
|
||||
|
||||
ReflowOutput childDesiredSize(aReflowInput);
|
||||
|
||||
WritingMode wm = inputAreaFrame->GetWritingMode();
|
||||
LogicalSize availSize = aReflowInput.ComputedSize(wm);
|
||||
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
|
||||
|
||||
ReflowInput childReflowOuput(aPresContext, aReflowInput,
|
||||
inputAreaFrame, availSize);
|
||||
|
||||
// Convert input area margin into my own writing-mode (in case it differs):
|
||||
LogicalMargin childMargin =
|
||||
childReflowOuput.ComputedLogicalMargin().ConvertTo(myWM, wm);
|
||||
|
||||
// offsets of input area frame within this frame:
|
||||
LogicalPoint
|
||||
childOffset(myWM,
|
||||
aReflowInput.ComputedLogicalBorderPadding().IStart(myWM) +
|
||||
childMargin.IStart(myWM),
|
||||
aReflowInput.ComputedLogicalBorderPadding().BStart(myWM) +
|
||||
childMargin.BStart(myWM));
|
||||
|
||||
nsReflowStatus childStatus;
|
||||
// We initially reflow the child with a dummy containerSize; positioning
|
||||
// will be fixed later.
|
||||
const nsSize dummyContainerSize;
|
||||
ReflowChild(inputAreaFrame, aPresContext, childDesiredSize,
|
||||
childReflowOuput, myWM, childOffset, dummyContainerSize, 0,
|
||||
childStatus);
|
||||
MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
|
||||
"We gave our child unconstrained available block-size, "
|
||||
"so it should be complete");
|
||||
|
||||
nscoord childMarginBoxBSize =
|
||||
childDesiredSize.BSize(myWM) + childMargin.BStartEnd(myWM);
|
||||
|
||||
if (contentBoxBSize == NS_INTRINSICSIZE) {
|
||||
// We are intrinsically sized -- we should shrinkwrap the input area's
|
||||
// block-size:
|
||||
contentBoxBSize = childMarginBoxBSize;
|
||||
|
||||
// Make sure we obey min/max-bsize in the case when we're doing intrinsic
|
||||
// sizing (we get it for free when we have a non-intrinsic
|
||||
// aReflowInput.ComputedBSize()). Note that we do this before
|
||||
// adjusting for borderpadding, since ComputedMaxBSize and
|
||||
// ComputedMinBSize are content heights.
|
||||
contentBoxBSize =
|
||||
NS_CSS_MINMAX(contentBoxBSize,
|
||||
aReflowInput.ComputedMinBSize(),
|
||||
aReflowInput.ComputedMaxBSize());
|
||||
|
||||
borderBoxBSize = contentBoxBSize +
|
||||
aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
|
||||
}
|
||||
|
||||
// Center child in block axis
|
||||
nscoord extraSpace = contentBoxBSize - childMarginBoxBSize;
|
||||
childOffset.B(myWM) += std::max(0, extraSpace / 2);
|
||||
|
||||
// Needed in FinishReflowChild, for logical-to-physical conversion:
|
||||
nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize).
|
||||
GetPhysicalSize(myWM);
|
||||
|
||||
// Place the child
|
||||
FinishReflowChild(inputAreaFrame, aPresContext, childDesiredSize,
|
||||
&childReflowOuput, myWM, childOffset, borderBoxSize, 0);
|
||||
|
||||
nsSize contentBoxSize =
|
||||
LogicalSize(myWM, contentBoxISize, contentBoxBSize).
|
||||
GetPhysicalSize(myWM);
|
||||
aDesiredSize.SetBlockStartAscent(
|
||||
childDesiredSize.BlockStartAscent() +
|
||||
inputAreaFrame->BStart(aReflowInput.GetWritingMode(),
|
||||
contentBoxSize));
|
||||
}
|
||||
|
||||
LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
|
||||
aDesiredSize.SetSize(myWM, logicalDesiredSize);
|
||||
|
||||
aDesiredSize.SetOverflowAreasToDesiredBounds();
|
||||
|
||||
if (inputAreaFrame) {
|
||||
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inputAreaFrame);
|
||||
}
|
||||
|
||||
FinishAndStoreOverflow(&aDesiredSize);
|
||||
|
||||
aStatus = NS_FRAME_COMPLETE;
|
||||
|
||||
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
||||
("exit nsDateTimeControlFrame::Reflow: size=%d,%d",
|
||||
aDesiredSize.Width(), aDesiredSize.Height()));
|
||||
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDateTimeControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
||||
{
|
||||
// Set up "datetimebox" XUL element which will be XBL-bound to the
|
||||
// actual controls.
|
||||
nsNodeInfoManager* nodeInfoManager =
|
||||
mContent->GetComposedDoc()->NodeInfoManager();
|
||||
RefPtr<NodeInfo> nodeInfo =
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::datetimebox, nullptr,
|
||||
kNameSpaceID_XUL, nsIDOMNode::ELEMENT_NODE);
|
||||
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
NS_TrustedNewXULElement(getter_AddRefs(mInputAreaContent), nodeInfo.forget());
|
||||
aElements.AppendElement(mInputAreaContent);
|
||||
|
||||
// Propogate our tabindex.
|
||||
nsAutoString tabIndexStr;
|
||||
if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr)) {
|
||||
mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::tabindex,
|
||||
tabIndexStr, false);
|
||||
}
|
||||
|
||||
// Propagate our readonly state.
|
||||
nsAutoString readonly;
|
||||
if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) {
|
||||
mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly,
|
||||
false);
|
||||
}
|
||||
|
||||
SyncDisabledState();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
||||
uint32_t aFilter)
|
||||
{
|
||||
if (mInputAreaContent) {
|
||||
aElements.AppendElement(mInputAreaContent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::SyncDisabledState()
|
||||
{
|
||||
EventStates eventStates = mContent->AsElement()->State();
|
||||
if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
|
||||
mInputAreaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
|
||||
EmptyString(), true);
|
||||
} else {
|
||||
mInputAreaContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDateTimeControlFrame::AttributeChanged(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
int32_t aModType)
|
||||
{
|
||||
NS_ASSERTION(mInputAreaContent, "The input area content must exist!");
|
||||
|
||||
// nsGkAtoms::disabled is handled by SyncDisabledState
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
if (aAttribute == nsGkAtoms::value ||
|
||||
aAttribute == nsGkAtoms::readonly ||
|
||||
aAttribute == nsGkAtoms::tabindex) {
|
||||
MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast");
|
||||
auto contentAsInputElem = static_cast<dom::HTMLInputElement*>(mContent);
|
||||
// If script changed the <input>'s type before setting these attributes
|
||||
// then we don't need to do anything since we are going to be reframed.
|
||||
if (contentAsInputElem->GetType() == NS_FORM_INPUT_TIME) {
|
||||
if (aAttribute == nsGkAtoms::value) {
|
||||
nsCOMPtr<nsIDateTimeInputArea> inputAreaContent =
|
||||
do_QueryInterface(mInputAreaContent);
|
||||
if (inputAreaContent) {
|
||||
nsContentUtils::AddScriptRunner(NewRunnableMethod(inputAreaContent,
|
||||
&nsIDateTimeInputArea::NotifyInputElementValueChanged));
|
||||
}
|
||||
} else {
|
||||
if (aModType == nsIDOMMutationEvent::REMOVAL) {
|
||||
mInputAreaContent->UnsetAttr(aNameSpaceID, aAttribute, true);
|
||||
} else {
|
||||
MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION ||
|
||||
aModType == nsIDOMMutationEvent::MODIFICATION);
|
||||
nsAutoString value;
|
||||
mContent->GetAttr(aNameSpaceID, aAttribute, value);
|
||||
mInputAreaContent->SetAttr(aNameSpaceID, aAttribute, value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
|
||||
aModType);
|
||||
}
|
||||
|
||||
void
|
||||
nsDateTimeControlFrame::ContentStatesChanged(EventStates aStates)
|
||||
{
|
||||
if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
|
||||
nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
|
||||
}
|
||||
}
|
||||
|
||||
nsIAtom*
|
||||
nsDateTimeControlFrame::GetType() const
|
||||
{
|
||||
return nsGkAtoms::dateTimeControlFrame;
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This frame type is used for input type=date, time, month, week, and
|
||||
* datetime-local.
|
||||
*
|
||||
* NOTE: some of the above-mentioned input types are still to-be-implemented.
|
||||
* See nsCSSFrameConstructor::FindInputData, as well as bug 1286182 (date),
|
||||
* bug 1306215 (month), bug 1306216 (week) and bug 1306217 (datetime-local).
|
||||
*/
|
||||
|
||||
#ifndef nsDateTimeControlFrame_h__
|
||||
#define nsDateTimeControlFrame_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
struct DateTimeValue;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsDateTimeControlFrame final : public nsContainerFrame,
|
||||
public nsIAnonymousContentCreator
|
||||
{
|
||||
typedef mozilla::dom::DateTimeValue DateTimeValue;
|
||||
|
||||
explicit nsDateTimeControlFrame(nsStyleContext* aContext);
|
||||
|
||||
public:
|
||||
friend nsIFrame* NS_NewDateTimeControlFrame(nsIPresShell* aPresShell,
|
||||
nsStyleContext* aContext);
|
||||
|
||||
void ContentStatesChanged(mozilla::EventStates aStates) override;
|
||||
void DestroyFrom(nsIFrame* aDestructRoot) override;
|
||||
|
||||
NS_DECL_QUERYFRAME_TARGET(nsDateTimeControlFrame)
|
||||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_FRAMEARENA_HELPERS
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
nsresult GetFrameName(nsAString& aResult) const override {
|
||||
return MakeFrameName(NS_LITERAL_STRING("DateTimeControl"), aResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsIAtom* GetType() const override;
|
||||
|
||||
bool IsFrameOfType(uint32_t aFlags) const override
|
||||
{
|
||||
return nsContainerFrame::IsFrameOfType(aFlags &
|
||||
~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
|
||||
}
|
||||
|
||||
// Reflow
|
||||
nscoord GetMinISize(nsRenderingContext* aRenderingContext) override;
|
||||
|
||||
nscoord GetPrefISize(nsRenderingContext* aRenderingContext) override;
|
||||
|
||||
void Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowState,
|
||||
nsReflowStatus& aStatus) override;
|
||||
|
||||
// nsIAnonymousContentCreator
|
||||
nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
|
||||
void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
||||
uint32_t aFilter) override;
|
||||
|
||||
nsresult AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
void UpdateInputBoxValue();
|
||||
void SetValueFromPicker(const DateTimeValue& aValue);
|
||||
void HandleFocusEvent();
|
||||
void HandleBlurEvent();
|
||||
void SetPickerState(bool aOpen);
|
||||
|
||||
private:
|
||||
class SyncDisabledStateEvent;
|
||||
friend class SyncDisabledStateEvent;
|
||||
class SyncDisabledStateEvent : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
explicit SyncDisabledStateEvent(nsDateTimeControlFrame* aFrame)
|
||||
: mFrame(aFrame)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
nsDateTimeControlFrame* frame =
|
||||
static_cast<nsDateTimeControlFrame*>(mFrame.GetFrame());
|
||||
NS_ENSURE_STATE(frame);
|
||||
|
||||
frame->SyncDisabledState();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsWeakFrame mFrame;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync the disabled state of the anonymous children up with our content's.
|
||||
*/
|
||||
void SyncDisabledState();
|
||||
|
||||
// Anonymous child which is bound via XBL to an element that wraps the input
|
||||
// area and reset button.
|
||||
nsCOMPtr<nsIContent> mInputAreaContent;
|
||||
};
|
||||
|
||||
#endif // nsDateTimeControlFrame_h__
|
|
@ -65,4 +65,6 @@ skip-if = toolkit == 'android' # Bug 1021644 - Fails when pushed into a differen
|
|||
skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
||||
[test_select_vertical.html]
|
||||
skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # Bug 1170129 - vertical <select> popup not implemented for e10s # <select> elements don't use an in-page popup on B2G/Android
|
||||
[test_bug1301290.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(resizing textarea not available in b2g) b2g-debug(resizing textarea not available in b2g) b2g-desktop(resizing textarea not available in b2g)
|
||||
[test_bug1305282.html]
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Bug 1301290</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style type="text/css">
|
||||
.blue, .green {
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
overflow: scroll;
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.blue {
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.green {
|
||||
background: green;
|
||||
margin-top: -100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="blue"></div>
|
||||
<textarea class="green" id="textarea"></textarea>
|
||||
<script type="application/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(() => SimpleTest.executeSoon(function() {
|
||||
var textarea = $("textarea");
|
||||
var rect = textarea.getBoundingClientRect();
|
||||
|
||||
synthesizeMouse(textarea, rect.width - 10, rect.height - 10, { type: "mousedown" });
|
||||
synthesizeMouse(textarea, rect.width + 40, rect.height + 40, { type: "mousemove" });
|
||||
synthesizeMouse(textarea, rect.width + 40, rect.height + 40, { type: "mouseup" });
|
||||
|
||||
var newrect = textarea.getBoundingClientRect();
|
||||
ok(newrect.width > rect.width, "width did not increase");
|
||||
ok(newrect.height > rect.height, "height did not increase");
|
||||
SimpleTest.finish();
|
||||
}));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -19,6 +19,7 @@ FRAME_ID(nsComboboxControlFrame)
|
|||
FRAME_ID(nsComboboxDisplayFrame)
|
||||
FRAME_ID(nsContainerFrame)
|
||||
FRAME_ID(nsContinuingTextFrame)
|
||||
FRAME_ID(nsDateTimeControlFrame)
|
||||
FRAME_ID(nsDeckFrame)
|
||||
FRAME_ID(nsDocElementBoxFrame)
|
||||
FRAME_ID(nsFieldSetFrame)
|
||||
|
|
|
@ -2937,7 +2937,7 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
|
|||
static int32_t
|
||||
MaxZIndexInList(nsDisplayList* aList, nsDisplayListBuilder* aBuilder)
|
||||
{
|
||||
int32_t maxZIndex = 0;
|
||||
int32_t maxZIndex = -1;
|
||||
for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
|
||||
maxZIndex = std::max(maxZIndex, item->ZIndex());
|
||||
}
|
||||
|
@ -2966,6 +2966,20 @@ MaxZIndexInListOfItemsContainedInFrame(nsDisplayList* aList, nsIFrame* aFrame)
|
|||
return maxZIndex;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void
|
||||
AppendInternalItemToTop(const nsDisplayListSet& aLists,
|
||||
T* aItem,
|
||||
int32_t aZIndex)
|
||||
{
|
||||
if (aZIndex >= 0) {
|
||||
aItem->SetOverrideZIndex(aZIndex);
|
||||
aLists.PositionedDescendants()->AppendNewToTop(aItem);
|
||||
} else {
|
||||
aLists.Content()->AppendNewToTop(aItem);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint32_t APPEND_OWN_LAYER = 0x1;
|
||||
static const uint32_t APPEND_POSITIONED = 0x2;
|
||||
static const uint32_t APPEND_SCROLLBAR_CONTAINER = 0x4;
|
||||
|
@ -2991,13 +3005,8 @@ AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
|
|||
// We want overlay scrollbars to always be on top of the scrolled content,
|
||||
// but we don't want them to unnecessarily cover overlapping elements from
|
||||
// outside our scroll frame.
|
||||
nsDisplayList* positionedDescendants = aLists.PositionedDescendants();
|
||||
if (!positionedDescendants->IsEmpty()) {
|
||||
newItem->SetOverrideZIndex(MaxZIndexInList(positionedDescendants, aBuilder));
|
||||
positionedDescendants->AppendNewToTop(newItem);
|
||||
} else {
|
||||
aLists.Outlines()->AppendNewToTop(newItem);
|
||||
}
|
||||
int32_t zIndex = MaxZIndexInList(aLists.PositionedDescendants(), aBuilder);
|
||||
AppendInternalItemToTop(aLists, newItem, zIndex);
|
||||
} else {
|
||||
aLists.BorderBackground()->AppendNewToTop(newItem);
|
||||
}
|
||||
|
@ -3573,17 +3582,9 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
|
||||
if (inactiveRegionItem) {
|
||||
nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
|
||||
nsDisplayList* destinationList = nullptr;
|
||||
int32_t zindex =
|
||||
MaxZIndexInListOfItemsContainedInFrame(positionedDescendants, mOuter);
|
||||
if (zindex >= 0) {
|
||||
destinationList = positionedDescendants;
|
||||
inactiveRegionItem->SetOverrideZIndex(zindex);
|
||||
} else {
|
||||
destinationList = scrolledContent.Outlines();
|
||||
}
|
||||
destinationList->AppendNewToTop(inactiveRegionItem);
|
||||
int32_t zIndex =
|
||||
MaxZIndexInListOfItemsContainedInFrame(scrolledContent.PositionedDescendants(), mOuter);
|
||||
AppendInternalItemToTop(scrolledContent, inactiveRegionItem, zIndex);
|
||||
}
|
||||
|
||||
if (aBuilder->ShouldBuildScrollInfoItemsForHoisting()) {
|
||||
|
|
|
@ -171,6 +171,8 @@ nsIFrame*
|
|||
NS_NewRangeFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame*
|
||||
NS_NewNumberControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame*
|
||||
NS_NewDateTimeControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsBlockFrame*
|
||||
NS_NewDetailsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="checkbox" style="-moz-appearance:none;">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<!-- Test: when switching to another type, the input element should look
|
||||
like that type (not like an input time element) -->
|
||||
<script type="text/javascript">
|
||||
function setToCheckbox()
|
||||
{
|
||||
document.getElementById("i").type = "checkbox";
|
||||
document.documentElement.className = "";
|
||||
}
|
||||
document.addEventListener("MozReftestInvalidate", setToCheckbox);
|
||||
</script>
|
||||
<body>
|
||||
<input type="time" id="i" style="-moz-appearance:none;">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
default-preferences pref(dom.forms.datetime,true)
|
||||
|
||||
# not valid on Android/B2G where type=time looks like type=text
|
||||
skip-if(Android||B2G||Mulet) != time-simple-unthemed.html time-simple-unthemed-ref.html
|
||||
skip-if(Android||B2G||Mulet) != time-large-font.html time-basic.html
|
||||
skip-if(Android||B2G||Mulet) != time-width-height.html time-basic.html
|
||||
skip-if(Android||B2G||Mulet) != time-border.html time-basic.html
|
||||
# only valid on Android/B2G where type=number looks the same as type=text
|
||||
skip-if(!Android&&!B2G&&!Mulet) == time-simple-unthemed.html time-simple-unthemed-ref.html
|
||||
|
||||
# type change
|
||||
skip-if(Android||B2G||Mulet) == to-time-from-other-type-unthemed.html time-simple-unthemed.html
|
||||
skip-if(Android||B2G||Mulet) == from-time-to-other-type-unthemed.html from-time-to-other-type-unthemed-ref.html
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" value="12:30">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" value="12:30" style="border:10px solid blue">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" value="12:30" style="font-size: 32px;">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="text" style="-moz-appearance:none;">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" style="-moz-appearance:none;">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="time" style="width:200px; height:50px">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<!-- Test: input element changed to time state doesn't look like checkbox state -->
|
||||
<script type="text/javascript">
|
||||
function setToTime()
|
||||
{
|
||||
document.getElementById("i").type = "time";
|
||||
document.documentElement.className = "";
|
||||
}
|
||||
document.addEventListener("MozReftestInvalidate", setToTime);
|
||||
</script>
|
||||
<body>
|
||||
<input type="checkbox" id="i" style="-moz-appearance:none;">
|
||||
</body>
|
||||
</html>
|
|
@ -11,3 +11,4 @@ include text/reftest.list
|
|||
include percentage/reftest.list
|
||||
include hidden/reftest.list
|
||||
include color/reftest.list
|
||||
include datetime/reftest.list
|
||||
|
|
|
@ -767,6 +767,13 @@ video > .caption-box {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* datetime elements */
|
||||
|
||||
input[type="time"] > xul|datetimebox {
|
||||
display: flex;
|
||||
-moz-binding: url("chrome://global/content/bindings/datetimebox.xml#time-input");
|
||||
}
|
||||
|
||||
/* details & summary */
|
||||
/* Need to revert Bug 1259889 Part 2 when removing details preference. */
|
||||
@supports -moz-bool-pref("dom.details_element.enabled") {
|
||||
|
|
|
@ -41,15 +41,14 @@ ClearKeySession::~ClearKeySession()
|
|||
{
|
||||
CK_LOGD("ClearKeySession dtor %p", this);
|
||||
|
||||
auto& keyIds = GetKeyIds();
|
||||
for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
|
||||
assert(ClearKeyDecryptionManager::Get()->HasSeenKeyId(*it));
|
||||
|
||||
ClearKeyDecryptionManager::Get()->ReleaseKeyId(*it);
|
||||
mCallback->KeyStatusChanged(&mSessionId[0], mSessionId.size(),
|
||||
&(*it)[0], it->size(),
|
||||
kGMPUnknown);
|
||||
std::vector<GMPMediaKeyInfo> key_infos;
|
||||
for (const KeyId& keyId : mKeyIds) {
|
||||
assert(ClearKeyDecryptionManager::Get()->HasSeenKeyId(keyId));
|
||||
ClearKeyDecryptionManager::Get()->ReleaseKeyId(keyId);
|
||||
key_infos.push_back(GMPMediaKeyInfo(&keyId[0], keyId.size(), kGMPUnknown));
|
||||
}
|
||||
mCallback->BatchedKeyStatusChanged(&mSessionId[0], mSessionId.size(),
|
||||
key_infos.data(), key_infos.size());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -168,24 +168,33 @@ ClearKeySessionManager::PersistentSessionDataLoaded(GMPErr aStatus,
|
|||
mSessions[aSessionId] = session;
|
||||
|
||||
uint32_t numKeys = aKeyDataSize / (2 * CLEARKEY_KEY_LEN);
|
||||
|
||||
vector<GMPMediaKeyInfo> key_infos;
|
||||
vector<KeyIdPair> keyPairs;
|
||||
for (uint32_t i = 0; i < numKeys; i ++) {
|
||||
const uint8_t* base = aKeyData + 2 * CLEARKEY_KEY_LEN * i;
|
||||
|
||||
KeyId keyId(base, base + CLEARKEY_KEY_LEN);
|
||||
assert(keyId.size() == CLEARKEY_KEY_LEN);
|
||||
KeyIdPair keyPair;
|
||||
|
||||
Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN);
|
||||
assert(key.size() == CLEARKEY_KEY_LEN);
|
||||
keyPair.mKeyId = KeyId(base, base + CLEARKEY_KEY_LEN);
|
||||
assert(keyPair.mKeyId.size() == CLEARKEY_KEY_LEN);
|
||||
|
||||
session->AddKeyId(keyId);
|
||||
keyPair.mKey = Key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN);
|
||||
assert(keyPair.mKey.size() == CLEARKEY_KEY_LEN);
|
||||
|
||||
mDecryptionManager->ExpectKeyId(keyId);
|
||||
mDecryptionManager->InitKey(keyId, key);
|
||||
mKeyIds.insert(key);
|
||||
mCallback->KeyStatusChanged(&aSessionId[0], aSessionId.size(),
|
||||
&keyId[0], keyId.size(),
|
||||
kGMPUsable);
|
||||
session->AddKeyId(keyPair.mKeyId);
|
||||
|
||||
mDecryptionManager->ExpectKeyId(keyPair.mKeyId);
|
||||
mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey);
|
||||
mKeyIds.insert(keyPair.mKey);
|
||||
|
||||
keyPairs.push_back(keyPair);
|
||||
key_infos.push_back(GMPMediaKeyInfo(&keyPairs[i].mKeyId[0],
|
||||
keyPairs[i].mKeyId.size(),
|
||||
kGMPUsable));
|
||||
}
|
||||
mCallback->BatchedKeyStatusChanged(&aSessionId[0], aSessionId.size(),
|
||||
key_infos.data(), key_infos.size());
|
||||
|
||||
mCallback->ResolveLoadSessionPromise(aPromiseId, true);
|
||||
}
|
||||
|
@ -216,13 +225,17 @@ ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
|
|||
return;
|
||||
}
|
||||
|
||||
for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) {
|
||||
mDecryptionManager->InitKey(it->mKeyId, it->mKey);
|
||||
mKeyIds.insert(it->mKeyId);
|
||||
mCallback->KeyStatusChanged(aSessionId, aSessionIdLength,
|
||||
&it->mKeyId[0], it->mKeyId.size(),
|
||||
kGMPUsable);
|
||||
vector<GMPMediaKeyInfo> key_infos;
|
||||
for (size_t i = 0; i < keyPairs.size(); i++) {
|
||||
KeyIdPair& keyPair = keyPairs[i];
|
||||
mDecryptionManager->InitKey(keyPair.mKeyId, keyPair.mKey);
|
||||
mKeyIds.insert(keyPair.mKeyId);
|
||||
key_infos.push_back(GMPMediaKeyInfo(&keyPair.mKeyId[0],
|
||||
keyPair.mKeyId.size(),
|
||||
kGMPUsable));
|
||||
}
|
||||
mCallback->BatchedKeyStatusChanged(aSessionId, aSessionIdLength,
|
||||
key_infos.data(), key_infos.size());
|
||||
|
||||
if (session->Type() != kGMPPersistentSession) {
|
||||
mCallback->ResolvePromise(aPromiseId);
|
||||
|
|
|
@ -101,7 +101,7 @@ FxAccountsPush.prototype = {
|
|||
_decodePushMessage(data) {
|
||||
Log.i("FxAccountsPush _decodePushMessage");
|
||||
data = JSON.parse(data);
|
||||
let { message, cryptoParams } = this._messageAndCryptoParams(data);
|
||||
let { headers, message } = this._messageAndHeaders(data);
|
||||
return new Promise((resolve, reject) => {
|
||||
PushService.getSubscription(FXA_PUSH_SCOPE,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
|
@ -112,22 +112,13 @@ FxAccountsPush.prototype = {
|
|||
return resolve(subscription);
|
||||
});
|
||||
}).then(subscription => {
|
||||
if (!cryptoParams) {
|
||||
return new Uint8Array();
|
||||
}
|
||||
return PushCrypto.decodeMsg(
|
||||
message,
|
||||
subscription.p256dhPrivateKey,
|
||||
new Uint8Array(subscription.getKey("p256dh")),
|
||||
cryptoParams.dh,
|
||||
cryptoParams.salt,
|
||||
cryptoParams.rs,
|
||||
new Uint8Array(subscription.getKey("auth")),
|
||||
cryptoParams.padSize
|
||||
);
|
||||
return PushCrypto.decrypt(subscription.p256dhPrivateKey,
|
||||
new Uint8Array(subscription.getKey("p256dh")),
|
||||
new Uint8Array(subscription.getKey("auth")),
|
||||
headers, message);
|
||||
})
|
||||
.then(decryptedMessage => {
|
||||
decryptedMessage = _decoder.decode(decryptedMessage);
|
||||
.then(plaintext => {
|
||||
let decryptedMessage = plaintext ? _decoder.decode(plaintext) : "";
|
||||
Messaging.sendRequestForResult({
|
||||
type: "FxAccountsPush:ReceivedPushMessageToDecode:Response",
|
||||
message: decryptedMessage
|
||||
|
@ -139,10 +130,10 @@ FxAccountsPush.prototype = {
|
|||
},
|
||||
|
||||
// Copied from PushServiceAndroidGCM
|
||||
_messageAndCryptoParams(data) {
|
||||
_messageAndHeaders(data) {
|
||||
// Default is no data (and no encryption).
|
||||
let message = null;
|
||||
let cryptoParams = null;
|
||||
let headers = null;
|
||||
|
||||
if (data.message && data.enc && (data.enckey || data.cryptokey)) {
|
||||
let headers = {
|
||||
|
@ -151,14 +142,13 @@ FxAccountsPush.prototype = {
|
|||
encryption: data.enc,
|
||||
encoding: data.con,
|
||||
};
|
||||
cryptoParams = getCryptoParams(headers);
|
||||
// Ciphertext is (urlsafe) Base 64 encoded.
|
||||
message = ChromeUtils.base64URLDecode(data.message, {
|
||||
// The Push server may append padding.
|
||||
padding: "ignore",
|
||||
});
|
||||
}
|
||||
return { message, cryptoParams };
|
||||
return { headers, message };
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "certdb.h"
|
||||
#include "hasht.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "pk11pub.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
|
@ -32,7 +33,6 @@ struct nsMyTrustedEVInfo
|
|||
const unsigned char ev_root_sha256_fingerprint[SHA256_LENGTH];
|
||||
const char* issuer_base64;
|
||||
const char* serial_base64;
|
||||
mozilla::UniqueCERTCertificate cert;
|
||||
};
|
||||
|
||||
// HOWTO enable additional CA root certificates for EV:
|
||||
|
@ -75,7 +75,6 @@ struct nsMyTrustedEVInfo
|
|||
// Remove all whitespaces. If you use multiple lines, make sure that
|
||||
// only the final line will be followed by a comma.
|
||||
// - the "Serial DER Base64" (as printed by pp)
|
||||
// - nullptr
|
||||
//
|
||||
// After adding an entry, test it locally against the test site that
|
||||
// has been provided by the CA. Note that you must use a version of NSS
|
||||
|
@ -125,7 +124,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
0x03, 0x2C, 0x7B, 0xDC, 0x09, 0x70, 0x76, 0x49, 0xBF, 0xAA },
|
||||
"MBExDzANBgNVBAMMBmV2cm9vdA==",
|
||||
"W9j5PS8YoKgynZdYa9i2Kwexnp8=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// This is an RSA root with an inadequate key size. It is used to test that
|
||||
|
@ -150,7 +148,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
0xE0, 0xDF, 0x1C, 0xAD, 0xB4, 0xC2, 0x76, 0xBB, 0x63, 0x24 },
|
||||
"MBsxGTAXBgNVBAMMEGV2X3Jvb3RfcnNhXzIwNDA=",
|
||||
"P1iIBgxk6kH+x64EUBTV3qoHuas=",
|
||||
nullptr
|
||||
},
|
||||
#endif
|
||||
{
|
||||
|
@ -165,7 +162,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"LixMVEQuMSowKAYDVQQLEyFTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVWIFJvb3RD"
|
||||
"QTE=",
|
||||
"AA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Cybertrust Global Root,O=Cybertrust, Inc
|
||||
|
@ -178,7 +174,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVz"
|
||||
"dCBHbG9iYWwgUm9vdA==",
|
||||
"BAAAAAABD4WqLUg=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=SwissSign Gold CA - G2,O=SwissSign AG,C=CH
|
||||
|
@ -191,7 +186,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEUxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMT"
|
||||
"FlN3aXNzU2lnbiBHb2xkIENBIC0gRzI=",
|
||||
"ALtAHEP1Xk+w",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
|
||||
|
@ -205,7 +199,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"EyJTZWN1cmUgRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBT"
|
||||
"dGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"AQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=StartCom Certification Authority,OU=Secure Digital Certificate Signing,O=StartCom Ltd.,C=IL
|
||||
|
@ -219,7 +212,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"EyJTZWN1cmUgRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBT"
|
||||
"dGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"LQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=StartCom Certification Authority G2,O=StartCom Ltd.,C=IL
|
||||
|
@ -232,7 +224,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFMxCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSwwKgYDVQQD"
|
||||
"EyNTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBHMg==",
|
||||
"Ow==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU="(c) 2006 VeriSign, Inc. - For authorized use only",OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
|
||||
|
@ -248,7 +239,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"PFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB"
|
||||
"dXRob3JpdHkgLSBHNQ==",
|
||||
"GNrRniZ96LtKIVjNzGs7Sg==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GeoTrust Primary Certification Authority,O=GeoTrust Inc.,C=US
|
||||
|
@ -261,7 +251,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQD"
|
||||
"EyhHZW9UcnVzdCBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5",
|
||||
"GKy1av1pthU6Y2yv2vrEoQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=thawte Primary Root CA,OU="(c) 2006 thawte, Inc. - For authorized use only",OU=Certification Services Division,O="thawte, Inc.",C=US
|
||||
|
@ -276,7 +265,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MjAwNiB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0G"
|
||||
"A1UEAxMWdGhhd3RlIFByaW1hcnkgUm9vdCBDQQ==",
|
||||
"NE7VVyDV7exJ9C/ON9srbQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=XRamp Global Certification Authority,O=XRamp Security Services Inc,OU=www.xrampsecurity.com,C=US
|
||||
|
@ -290,7 +278,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMT"
|
||||
"JFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"UJRs7Bjq1ZxN1ZfvdY+grQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=SecureTrust CA,O=SecureTrust Corporation,C=US
|
||||
|
@ -303,7 +290,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlv"
|
||||
"bjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=",
|
||||
"DPCOXAgWpa1Cf/DrJxhZ0A==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Secure Global CA,O=SecureTrust Corporation,C=US
|
||||
|
@ -316,7 +302,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEoxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlv"
|
||||
"bjEZMBcGA1UEAxMQU2VjdXJlIEdsb2JhbCBDQQ==",
|
||||
"B1YipOjUiolN9BPI8PjqpQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=COMODO ECC Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
|
||||
|
@ -330,7 +315,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkG"
|
||||
"A1UEAxMiQ09NT0RPIEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"H0evqmIAcFBUTAGem2OZKg==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=COMODO Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
|
||||
|
@ -344,7 +328,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEnMCUG"
|
||||
"A1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5",
|
||||
"ToEtioJl4AsC7j41AkblPQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE
|
||||
|
@ -358,7 +341,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"QWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0"
|
||||
"IEV4dGVybmFsIENBIFJvb3Q=",
|
||||
"AQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US
|
||||
|
@ -373,7 +355,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"GGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEfMB0GA1UEAxMWVVROLVVTRVJGaXJz"
|
||||
"dC1IYXJkd2FyZQ==",
|
||||
"RL4Mi1AAJLQR0zYq/mUK/Q==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// OU=Go Daddy Class 2 Certification Authority,O=\"The Go Daddy Group, Inc.\",C=US
|
||||
|
@ -387,7 +368,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"Yy4xMTAvBgNVBAsTKEdvIERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRo"
|
||||
"b3JpdHk=",
|
||||
"AA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Go Daddy Root Certificate Authority - G2,O="GoDaddy.com, Inc.",L=Scottsdale,ST=Arizona,C=US
|
||||
|
@ -401,7 +381,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"dHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xMTAvBgNVBAMTKEdv"
|
||||
"IERhZGR5IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzI=",
|
||||
"AA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// OU=Starfield Class 2 Certification Authority,O=\"Starfield Technologies, Inc.\",C=US
|
||||
|
@ -415,7 +394,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"LCBJbmMuMTIwMAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9u"
|
||||
"IEF1dGhvcml0eQ==",
|
||||
"AA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Starfield Root Certificate Authority - G2,O="Starfield Technologies, Inc.",L=Scottsdale,ST=Arizona,C=US
|
||||
|
@ -430,7 +408,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MDAGA1UEAxMpU3RhcmZpZWxkIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0g"
|
||||
"RzI=",
|
||||
"AA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
|
||||
|
@ -444,7 +421,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"EHd3dy5kaWdpY2VydC5jb20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJh"
|
||||
"bmNlIEVWIFJvb3QgQ0E=",
|
||||
"AqxcJmoLQJuPC3nyrkYldw==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=QuoVadis Root CA 2,O=QuoVadis Limited,C=BM
|
||||
|
@ -457,7 +433,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYD"
|
||||
"VQQDExJRdW9WYWRpcyBSb290IENBIDI=",
|
||||
"BQk=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
|
||||
|
@ -471,7 +446,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"Qy4xMDAuBgNVBAMTJ05ldHdvcmsgU29sdXRpb25zIENlcnRpZmljYXRlIEF1dGhv"
|
||||
"cml0eQ==",
|
||||
"V8szb8JcFuZHFhfjkDFo4A==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Entrust Root Certification Authority,OU="(c) 2006 Entrust, Inc.",OU=www.entrust.net/CPS is incorporated by reference,O="Entrust, Inc.",C=US
|
||||
|
@ -486,7 +460,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"bmNlMR8wHQYDVQQLExYoYykgMjAwNiBFbnRydXN0LCBJbmMuMS0wKwYDVQQDEyRF"
|
||||
"bnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=",
|
||||
"RWtQVA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE
|
||||
|
@ -499,7 +472,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYD"
|
||||
"VQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=",
|
||||
"BAAAAAABFUtaw5Q=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R2
|
||||
|
@ -512,7 +484,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpH"
|
||||
"bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu",
|
||||
"BAAAAAABD4Ym5g0=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R3
|
||||
|
@ -525,7 +496,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpH"
|
||||
"bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu",
|
||||
"BAAAAAABIVhTCKI=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Buypass Class 3 Root CA,O=Buypass AS-983163327,C=NO
|
||||
|
@ -538,7 +508,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"ME4xCzAJBgNVBAYTAk5PMR0wGwYDVQQKDBRCdXlwYXNzIEFTLTk4MzE2MzMyNzEg"
|
||||
"MB4GA1UEAwwXQnV5cGFzcyBDbGFzcyAzIFJvb3QgQ0E=",
|
||||
"Ag==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Class 2 Primary CA,O=Certplus,C=FR
|
||||
|
@ -551,7 +520,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xh"
|
||||
"c3MgMiBQcmltYXJ5IENB",
|
||||
"AIW9S/PY2uNp9pTXX8OlRCM=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Chambers of Commerce Root - 2008,O=AC Camerfirma S.A.,serialNumber=A82743287,L=Madrid (see current address at www.camerfirma.com/address),C=EU
|
||||
|
@ -566,7 +534,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMT"
|
||||
"IENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4",
|
||||
"AKPaQn6ksa7a",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Global Chambersign Root - 2008,O=AC Camerfirma S.A.,serialNumber=A82743287,L=Madrid (see current address at www.camerfirma.com/address),C=EU
|
||||
|
@ -581,7 +548,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMT"
|
||||
"Hkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwOA==",
|
||||
"AMnN0+nVfSPO",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=AffirmTrust Commercial,O=AffirmTrust,C=US
|
||||
|
@ -594,7 +560,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEQxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEfMB0GA1UEAwwW"
|
||||
"QWZmaXJtVHJ1c3QgQ29tbWVyY2lhbA==",
|
||||
"d3cGJyapsXw=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=AffirmTrust Networking,O=AffirmTrust,C=US
|
||||
|
@ -607,7 +572,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEQxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEfMB0GA1UEAwwW"
|
||||
"QWZmaXJtVHJ1c3QgTmV0d29ya2luZw==",
|
||||
"fE8EORzUmS0=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=AffirmTrust Premium,O=AffirmTrust,C=US
|
||||
|
@ -620,7 +584,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEExCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEcMBoGA1UEAwwT"
|
||||
"QWZmaXJtVHJ1c3QgUHJlbWl1bQ==",
|
||||
"bYwURrGmCu4=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=AffirmTrust Premium ECC,O=AffirmTrust,C=US
|
||||
|
@ -633,7 +596,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwX"
|
||||
"QWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0M=",
|
||||
"dJclisc/elQ=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Certum Trusted Network CA,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL
|
||||
|
@ -647,7 +609,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAg"
|
||||
"BgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0E=",
|
||||
"BETA",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Certum Trusted Network CA 2,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL
|
||||
|
@ -661,7 +622,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSQw"
|
||||
"IgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBIDI=",
|
||||
"IdbQSk8lD8kyN/yqXhKN6Q==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Izenpe.com,O=IZENPE S.A.,C=ES
|
||||
|
@ -674,7 +634,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MDgxCzAJBgNVBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwK"
|
||||
"SXplbnBlLmNvbQ==",
|
||||
"ALC3WhZIX7/hy/WL1xnmfQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Izenpe.com,O=IZENPE S.A.,C=ES
|
||||
|
@ -687,7 +646,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MDgxCzAJBgNVBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwK"
|
||||
"SXplbnBlLmNvbQ==",
|
||||
"ALC3WhZIX7/hy/WL1xnmfQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=T-TeleSec GlobalRoot Class 3,OU=T-Systems Trust Center,O=T-Systems Enterprise Services GmbH,C=DE
|
||||
|
@ -701,7 +659,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"U2VydmljZXMgR21iSDEfMB0GA1UECwwWVC1TeXN0ZW1zIFRydXN0IENlbnRlcjEl"
|
||||
"MCMGA1UEAwwcVC1UZWxlU2VjIEdsb2JhbFJvb3QgQ2xhc3MgMw==",
|
||||
"AQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=China Internet Network Information Center EV Certificates Root,O=China Internet Network Information Center,C=CN
|
||||
|
@ -715,7 +672,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"ayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNoaW5hIEludGVybmV0IE5l"
|
||||
"dHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRlcyBSb290",
|
||||
"SJ8AAQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=TWCA Root Certification Authority,OU=Root CA,O=TAIWAN-CA,C=TW
|
||||
|
@ -729,7 +685,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"b3QgQ0ExKjAoBgNVBAMMIVRXQ0EgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0"
|
||||
"eQ==",
|
||||
"AQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=D-TRUST Root Class 3 CA 2 EV 2009,O=D-Trust GmbH,C=DE
|
||||
|
@ -742,7 +697,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMM"
|
||||
"IUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOQ==",
|
||||
"CYP0",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Swisscom Root EV CA 2,OU=Digital Certificate Services,O=Swisscom,C=ch
|
||||
|
@ -756,7 +710,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEeMBwGA1UEAxMVU3dpc3Njb20gUm9v"
|
||||
"dCBFViBDQSAy",
|
||||
"APL6ZOJ0Y9ON/RAdBB92ylg=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=VeriSign Universal Root Certification Authority,OU="(c) 2008 VeriSign, Inc. - For authorized use only",OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
|
||||
|
@ -771,7 +724,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"cmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMT"
|
||||
"L1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5",
|
||||
"QBrEZCGzEyEDDrvkEhrFHQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GeoTrust Primary Certification Authority - G3,OU=(c) 2008 GeoTrust Inc. - For authorized use only,O=GeoTrust Inc.,C=US
|
||||
|
@ -786,7 +738,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"bmx5MTYwNAYDVQQDEy1HZW9UcnVzdCBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0"
|
||||
"aG9yaXR5IC0gRzM=",
|
||||
"FaxulBmyeUtB9iepwxgPHw==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=thawte Primary Root CA - G3,OU="(c) 2008 thawte, Inc. - For authorized use only",OU=Certification Services Division,O="thawte, Inc.",C=US
|
||||
|
@ -801,7 +752,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG"
|
||||
"A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz",
|
||||
"YAGXt0an6rS0mtZLL/eQ+w==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN = Autoridad de Certificacion Firmaprofesional CIF A62634068, C = ES
|
||||
|
@ -814,7 +764,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNh"
|
||||
"Y2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=",
|
||||
"U+w77vuySF8=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN = TWCA Global Root CA, OU = Root CA, O = TAIWAN-CA, C = TW
|
||||
|
@ -827,7 +776,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jv"
|
||||
"b3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0E=",
|
||||
"DL4=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN = E-Tugra Certification Authority, OU = E-Tugra Sertifikasyon Merkezi, O = E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş., L = Ankara, C = TR
|
||||
|
@ -842,7 +790,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"xZ4uMSYwJAYDVQQLDB1FLVR1Z3JhIFNlcnRpZmlrYXN5b24gTWVya2V6aTEoMCYG"
|
||||
"A1UEAwwfRS1UdWdyYSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"amg+nFGby1M=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Actalis Authentication Root CA,O=Actalis S.p.A./03358520967,L=Milan,C=IT
|
||||
|
@ -856,7 +803,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"cyBTLnAuQS4vMDMzNTg1MjA5NjcxJzAlBgNVBAMMHkFjdGFsaXMgQXV0aGVudGlj"
|
||||
"YXRpb24gUm9vdCBDQQ==",
|
||||
"VwoRl0LE48w=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Certification Authority of WoSign,O=WoSign CA Limited,C=CN
|
||||
|
@ -869,7 +815,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEqMCgG"
|
||||
"A1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWdu",
|
||||
"XmjWEXGUY1BWAGjzPsnFkQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=CA ...............,O=WoSign CA Limited,C=CN
|
||||
|
@ -882,7 +827,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkG"
|
||||
"A1UEAwwSQ0Eg5rKD6YCa5qC56K+B5Lmm",
|
||||
"UHBrzdgT/BtOOzNy0hFIjQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=DigiCert Assured ID Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US
|
||||
|
@ -896,7 +840,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg"
|
||||
"Um9vdCBHMg==",
|
||||
"C5McOtY5Z+pnI7/Dr5r0Sw==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=DigiCert Assured ID Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US
|
||||
|
@ -910,7 +853,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg"
|
||||
"Um9vdCBHMw==",
|
||||
"C6Fa+h3foLVJRK/NJKBs7A==",
|
||||
nullptr,
|
||||
},
|
||||
{
|
||||
// CN=DigiCert Global Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US
|
||||
|
@ -924,7 +866,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"EHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290"
|
||||
"IEcy",
|
||||
"Azrx5qcRqaC7KGSxHQn65Q==",
|
||||
nullptr,
|
||||
},
|
||||
{
|
||||
// CN=DigiCert Global Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US
|
||||
|
@ -938,7 +879,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"EHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290"
|
||||
"IEcz",
|
||||
"BVVWvPJepDU1w6QP1atFcg==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=DigiCert Trusted Root G4,OU=www.digicert.com,O=DigiCert Inc,C=US
|
||||
|
@ -952,7 +892,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"EHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9v"
|
||||
"dCBHNA==",
|
||||
"BZsbV56OITLiOQe9p3d1XA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=QuoVadis Root CA 2 G3,O=QuoVadis Limited,C=BM
|
||||
|
@ -965,7 +904,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYD"
|
||||
"VQQDExVRdW9WYWRpcyBSb290IENBIDIgRzM=",
|
||||
"RFc0JFuBiZs18s64KztbpybwdSg=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
|
||||
|
@ -979,7 +917,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkG"
|
||||
"A1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"TKr5yttjb+Af907YWwOGnQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
|
||||
|
@ -993,7 +930,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG"
|
||||
"A1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"Af1tMPyjylGoG7xkDjUDLQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=USERTrust ECC Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
|
||||
|
@ -1007,7 +943,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG"
|
||||
"A1UEAxMlVVNFUlRydXN0IEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
|
||||
"XIuZxVqUxdJxVt7NiYDMJg==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R4
|
||||
|
@ -1020,7 +955,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UE"
|
||||
"ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==",
|
||||
"KjikHJYKBN5CsiilC+g0mAI=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R5
|
||||
|
@ -1033,7 +967,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNTETMBEGA1UE"
|
||||
"ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==",
|
||||
"YFlJ4CYuu1X5CneKcflK2Gw=",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Entrust.net Certification Authority (2048),OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),O=Entrust.net
|
||||
|
@ -1048,7 +981,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"A1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50"
|
||||
"cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp",
|
||||
"OGPe+A==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL
|
||||
|
@ -1061,7 +993,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4x"
|
||||
"KTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBSb290IENB",
|
||||
"AJiWjQ==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Entrust Root Certification Authority - G2,OU="(c) 2009 Entrust, Inc. - for authorized use only",OU=See www.entrust.net/legal-terms,O="Entrust, Inc.",C=US
|
||||
|
@ -1077,7 +1008,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MAYDVQQDEylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH"
|
||||
"Mg==",
|
||||
"SlOMKA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Entrust Root Certification Authority - EC1,OU="(c) 2012 Entrust, Inc. - for authorized use only",OU=See www.entrust.net/legal-terms,O="Entrust, Inc.",C=US
|
||||
|
@ -1093,7 +1023,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MQYDVQQDEypFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBF"
|
||||
"QzE=",
|
||||
"AKaLeSkAAAAAUNCR+Q==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=CFCA EV ROOT,O=China Financial Certification Authority,C=CN
|
||||
|
@ -1106,7 +1035,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFYxCzAJBgNVBAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlm"
|
||||
"aWNhdGlvbiBBdXRob3JpdHkxFTATBgNVBAMMDENGQ0EgRVYgUk9PVA==",
|
||||
"GErM1g==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Certification Authority of WoSign G2,O=WoSign CA Limited,C=CN
|
||||
|
@ -1119,7 +1047,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MFgxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEtMCsG"
|
||||
"A1UEAxMkQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWduIEcy",
|
||||
"ayXaioidfLwPBbOxemFFRA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=CA WoSign ECC Root,O=WoSign CA Limited,C=CN
|
||||
|
@ -1132,7 +1059,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkG"
|
||||
"A1UEAxMSQ0EgV29TaWduIEVDQyBSb290",
|
||||
"aEpYcIBr8I8C+vbe6LCQkA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6,O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A...,L=Ankara,C=TR
|
||||
|
@ -1147,7 +1073,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"SGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlr"
|
||||
"IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2",
|
||||
"faHyZeyK",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// OU=Security Communication RootCA2,O="SECOM Trust Systems CO.,LTD.",C=JP
|
||||
|
@ -1160,7 +1085,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENP"
|
||||
"LixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=",
|
||||
"AA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=OISTE WISeKey Global Root GB CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH
|
||||
|
@ -1174,7 +1098,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"RSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds"
|
||||
"b2JhbCBSb290IEdCIENB",
|
||||
"drEgUnTwhYdGs/gjGvbCwA==",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Certplus Root CA G1,O=Certplus,C=FR
|
||||
|
@ -1187,7 +1110,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy"
|
||||
"dHBsdXMgUm9vdCBDQSBHMQ==",
|
||||
"ESBVg+QtPlRWhS2DN7cs3EYR",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=Certplus Root CA G2,O=Certplus,C=FR
|
||||
|
@ -1200,7 +1122,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy"
|
||||
"dHBsdXMgUm9vdCBDQSBHMg==",
|
||||
"ESDZkc6uo+jF5//pAq/Pc7xV",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=OpenTrust Root CA G1,O=OpenTrust,C=FR
|
||||
|
@ -1213,7 +1134,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w"
|
||||
"ZW5UcnVzdCBSb290IENBIEcx",
|
||||
"ESCzkFU5fX82bWTCp59rY45n",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=OpenTrust Root CA G2,O=OpenTrust,C=FR
|
||||
|
@ -1226,7 +1146,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w"
|
||||
"ZW5UcnVzdCBSb290IENBIEcy",
|
||||
"ESChaRu/vbm9UpaPI+hIvyYR",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=OpenTrust Root CA G3,O=OpenTrust,C=FR
|
||||
|
@ -1239,7 +1158,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w"
|
||||
"ZW5UcnVzdCBSb290IENBIEcz",
|
||||
"ESDm+Ez8JLC+BUCs2oMbNGA/",
|
||||
nullptr
|
||||
},
|
||||
{
|
||||
// CN=VeriSign Class 3 Public Primary Certification Authority - G4,OU="(c) 2007 VeriSign, Inc. - For authorized use only",OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US
|
||||
|
@ -1255,7 +1173,6 @@ static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
|
|||
"PFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB"
|
||||
"dXRob3JpdHkgLSBHNA==",
|
||||
"L4D+I4wOIg9IZxIokYessw==",
|
||||
nullptr
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1301,19 +1218,31 @@ CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
|
|||
return false;
|
||||
}
|
||||
|
||||
unsigned char fingerprint[SHA256_LENGTH];
|
||||
SECStatus srv =
|
||||
PK11_HashBuf(SEC_OID_SHA256, fingerprint, cert->derCert.data,
|
||||
AssertedCast<int32_t>(cert->derCert.len));
|
||||
if (srv != SECSuccess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SECOidData* cabforumOIDData = SECOID_FindOIDByTag(sCABForumEVOIDTag);
|
||||
for (const nsMyTrustedEVInfo& entry : myTrustedEVInfos) {
|
||||
if (entry.cert && CERT_CompareCerts(cert.get(), entry.cert.get())) {
|
||||
if (cabforumOIDData && cabforumOIDData->oid.len == policy.numBytes &&
|
||||
mozilla::PodEqual(cabforumOIDData->oid.data, policy.bytes,
|
||||
policy.numBytes)) {
|
||||
return true;
|
||||
}
|
||||
const SECOidData* oidData = SECOID_FindOIDByTag(entry.oid_tag);
|
||||
if (oidData && oidData->oid.len == policy.numBytes &&
|
||||
mozilla::PodEqual(oidData->oid.data, policy.bytes, policy.numBytes)) {
|
||||
return true;
|
||||
}
|
||||
// This check ensures that only the specific roots we approve for EV get
|
||||
// that status, and not certs (roots or otherwise) that happen to have an
|
||||
// OID that's already been approved for EV.
|
||||
if (!PodEqual(fingerprint, entry.ev_root_sha256_fingerprint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cabforumOIDData && cabforumOIDData->oid.len == policy.numBytes &&
|
||||
PodEqual(cabforumOIDData->oid.data, policy.bytes, policy.numBytes)) {
|
||||
return true;
|
||||
}
|
||||
const SECOidData* oidData = SECOID_FindOIDByTag(entry.oid_tag);
|
||||
if (oidData && oidData->oid.len == policy.numBytes &&
|
||||
PodEqual(oidData->oid.data, policy.bytes, policy.numBytes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1360,14 +1289,14 @@ IdentityInfoInit()
|
|||
ias.serialNumber.len = serialNumber.len;
|
||||
ias.serialNumber.type = siUnsignedInteger;
|
||||
|
||||
entry.cert = UniqueCERTCertificate(CERT_FindCertByIssuerAndSN(nullptr, &ias));
|
||||
UniqueCERTCertificate cert(CERT_FindCertByIssuerAndSN(nullptr, &ias));
|
||||
|
||||
// If an entry is missing in the NSS root database, it may be because the
|
||||
// root database is out of sync with what we expect (e.g. a different
|
||||
// version of system NSS is installed). We assert on debug builds, but
|
||||
// silently continue on release builds. In both cases, the root cert does
|
||||
// not get EV treatment.
|
||||
if (!entry.cert) {
|
||||
if (!cert) {
|
||||
#ifdef DEBUG
|
||||
// The debug CA structs are at positions 0 to NUM_TEST_EV_ROOTS - 1, and
|
||||
// are NOT in the NSS root DB.
|
||||
|
@ -1380,9 +1309,8 @@ IdentityInfoInit()
|
|||
}
|
||||
|
||||
unsigned char certFingerprint[SHA256_LENGTH];
|
||||
rv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint,
|
||||
entry.cert->derCert.data,
|
||||
static_cast<int32_t>(entry.cert->derCert.len));
|
||||
rv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint, cert->derCert.data,
|
||||
AssertedCast<int32_t>(cert->derCert.len));
|
||||
PR_ASSERT(rv == SECSuccess);
|
||||
if (rv == SECSuccess) {
|
||||
bool same = !memcmp(certFingerprint, entry.ev_root_sha256_fingerprint,
|
||||
|
@ -1405,7 +1333,6 @@ IdentityInfoInit()
|
|||
}
|
||||
|
||||
if (rv != SECSuccess) {
|
||||
entry.cert = nullptr;
|
||||
entry.oid_tag = SEC_OID_UNKNOWN;
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
@ -1425,10 +1352,6 @@ EnsureIdentityInfoLoaded()
|
|||
void
|
||||
CleanupIdentityInfo()
|
||||
{
|
||||
for (nsMyTrustedEVInfo& entry : myTrustedEVInfos) {
|
||||
entry.cert = nullptr;
|
||||
}
|
||||
|
||||
memset(&sIdentityInfoCallOnce, 0, sizeof(PRCallOnceType));
|
||||
}
|
||||
|
||||
|
|
|
@ -268,6 +268,16 @@ browser.Context = class {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the OS window.
|
||||
*/
|
||||
get position() {
|
||||
return {
|
||||
x: this.window.screenX,
|
||||
y: this.window.screenY,
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1230,12 +1230,10 @@ GeckoDriver.prototype.getChromeWindowHandles = function(cmd, resp) {
|
|||
* Get the current window position.
|
||||
*
|
||||
* @return {Object.<string, number>}
|
||||
* Object with x and y coordinates.
|
||||
* Object with |x| and |y| coordinates.
|
||||
*/
|
||||
GeckoDriver.prototype.getWindowPosition = function(cmd, resp) {
|
||||
let win = this.getCurrentWindow();
|
||||
resp.body.x = win.screenX;
|
||||
resp.body.y = win.screenY;
|
||||
return this.curBrowser.position;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1247,20 +1245,25 @@ GeckoDriver.prototype.getWindowPosition = function(cmd, resp) {
|
|||
* @param {number} y
|
||||
* Y coordinate of the top/left of the window that it will be
|
||||
* moved to.
|
||||
*
|
||||
* @return {Object.<string, number>}
|
||||
* Object with |x| and |y| coordinates.
|
||||
*/
|
||||
GeckoDriver.prototype.setWindowPosition = function(cmd, resp) {
|
||||
if (this.appName != "Firefox") {
|
||||
throw new WebDriverError("Unable to set the window position on mobile");
|
||||
throw new UnsupportedOperationError("Unable to set the window position on mobile");
|
||||
}
|
||||
|
||||
let x = parseInt(cmd.parameters.x);
|
||||
let y = parseInt(cmd.parameters.y);
|
||||
if (isNaN(x) || isNaN(y)) {
|
||||
throw new UnknownError("x and y arguments should be integers");
|
||||
let {x, y} = cmd.parameters;
|
||||
if (!Number.isInteger(x) || !Number.isInteger(y) ||
|
||||
x < 0 || y < 0) {
|
||||
throw new InvalidArgumentError();
|
||||
}
|
||||
|
||||
let win = this.getCurrentWindow();
|
||||
win.moveTo(x, y);
|
||||
|
||||
return this.curBrowser.position;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,22 +13,28 @@
|
|||
#limitations under the License.
|
||||
|
||||
from marionette import MarionetteTestCase
|
||||
from marionette_driver.errors import MarionetteException
|
||||
from marionette_driver.errors import InvalidArgumentException
|
||||
|
||||
class TestWindowPosition(MarionetteTestCase):
|
||||
|
||||
def test_that_we_return_the_window_position(self):
|
||||
def test_get_types(self):
|
||||
position = self.marionette.get_window_position()
|
||||
self.assertTrue(isinstance(position['x'], int))
|
||||
self.assertTrue(isinstance(position['y'], int))
|
||||
self.assertTrue(isinstance(position["x"], int))
|
||||
self.assertTrue(isinstance(position["y"], int))
|
||||
|
||||
def test_that_we_can_set_the_window_position(self):
|
||||
def test_set_types(self):
|
||||
for x, y in (["a", "b"], [1.2, 3.4], [True, False], [[], []], [{}, {}]):
|
||||
with self.assertRaises(InvalidArgumentException):
|
||||
self.marionette.set_window_position(x, y)
|
||||
|
||||
def test_out_of_bounds_arguments(self):
|
||||
with self.assertRaises(InvalidArgumentException):
|
||||
self.marionette.set_window_position(-1, 0)
|
||||
with self.assertRaises(InvalidArgumentException):
|
||||
self.marionette.set_window_position(0, -1)
|
||||
|
||||
def test_move(self):
|
||||
old_position = self.marionette.get_window_position()
|
||||
new_position = {"x": old_position['x'] + 10, "y": old_position['y'] + 10}
|
||||
self.marionette.set_window_position(new_position['x'], new_position['y'])
|
||||
self.assertNotEqual(old_position['x'], new_position['x'])
|
||||
self.assertNotEqual(old_position['y'], new_position['y'])
|
||||
|
||||
def test_that_we_can_get_an_error_when_passing_something_other_than_integers(self):
|
||||
self.assertRaises(MarionetteException, self.marionette.set_window_position, "a","b")
|
||||
|
||||
new_position = {"x": old_position["x"] + 10, "y": old_position["y"] + 10}
|
||||
self.marionette.set_window_position(new_position["x"], new_position["y"])
|
||||
self.assertNotEqual(old_position['x'], new_position["x"])
|
||||
self.assertNotEqual(old_position['y'], new_position["y"])
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[encrypted-media-keystatuses.html]
|
||||
type: testharness
|
||||
[Verify MediaKeySession.keyStatuses.]
|
||||
expected: FAIL
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
"preprocess": "localize"
|
||||
},
|
||||
|
||||
"creator": {
|
||||
"author": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"preprocess": "localize"
|
||||
|
@ -180,7 +180,25 @@
|
|||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"optional": true
|
||||
},
|
||||
|
||||
"developer": {
|
||||
"type": "object",
|
||||
"optional": true,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"preprocess": "localize"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"preprocess": "localize"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"additionalProperties": { "$ref": "UnrecognizedProperty" }
|
||||
|
|
|
@ -167,7 +167,7 @@ function setupFormHistory(aCallback) {
|
|||
{ op : "add", fieldname : "field9", value : "value" },
|
||||
{ op : "add", fieldname : "field10", value : "42" },
|
||||
{ op : "add", fieldname : "field11", value : "2010-10-10" },
|
||||
{ op : "add", fieldname : "field12", value : "21:21" },
|
||||
{ op : "add", fieldname : "field12", value : "21:21" }, // not used, since type=time doesn't have autocomplete currently
|
||||
{ op : "add", fieldname : "field13", value : "32" }, // not used, since type=range doesn't have a drop down menu
|
||||
{ op : "add", fieldname : "field14", value : "#ffffff" }, // not used, since type=color doesn't have autocomplete currently
|
||||
{ op : "add", fieldname : "field15", value : "2016-08" },
|
||||
|
@ -913,15 +913,13 @@ function runTest() {
|
|||
|
||||
input = $_(15, "field12");
|
||||
restoreForm();
|
||||
expectPopup();
|
||||
doKey("down");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 406:
|
||||
checkMenuEntries(["21:21"]);
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("21:21");
|
||||
checkMenuEntries([]); // type=time with it's own control frame does not
|
||||
// have a drop down menu for now
|
||||
checkForm("");
|
||||
|
||||
input = $_(16, "field13");
|
||||
restoreForm();
|
||||
|
|
|
@ -168,6 +168,26 @@ TableUpdateV4::NewPrefixes(int32_t aSize, std::string& aPrefixes)
|
|||
NS_ENSURE_TRUE_VOID(aPrefixes.size() % aSize == 0);
|
||||
NS_ENSURE_TRUE_VOID(!mPrefixesMap.Get(aSize));
|
||||
|
||||
if (LOG_ENABLED() && 4 == aSize) {
|
||||
int numOfPrefixes = aPrefixes.size() / 4;
|
||||
uint32_t* p = (uint32_t*)aPrefixes.c_str();
|
||||
|
||||
// Dump the first/last 10 fixed-length prefixes for debugging.
|
||||
LOG(("* The first 10 (maximum) fixed-length prefixes: "));
|
||||
for (int i = 0; i < std::min(10, numOfPrefixes); i++) {
|
||||
uint8_t* c = (uint8_t*)&p[i];
|
||||
LOG(("%.2X%.2X%.2X%.2X", c[0], c[1], c[2], c[3]));
|
||||
}
|
||||
|
||||
LOG(("* The last 10 (maximum) fixed-length prefixes: "));
|
||||
for (int i = std::max(0, numOfPrefixes - 10); i < numOfPrefixes; i++) {
|
||||
uint8_t* c = (uint8_t*)&p[i];
|
||||
LOG(("%.2X%.2X%.2X%.2X", c[0], c[1], c[2], c[3]));
|
||||
}
|
||||
|
||||
LOG(("---- %d fixed-length prefixes in total.", aPrefixes.size() / aSize));
|
||||
}
|
||||
|
||||
PrefixStdString* prefix = new PrefixStdString(aPrefixes);
|
||||
mPrefixesMap.Put(aSize, prefix);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "nsUrlClassifierUtils.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "RiceDeltaDecoder.h"
|
||||
#include "mozilla/EndianUtils.h"
|
||||
|
||||
// MOZ_LOG=UrlClassifierProtocolParser:5
|
||||
mozilla::LazyLogModule gUrlClassifierProtocolParserLog("UrlClassifierProtocolParser");
|
||||
|
@ -908,8 +910,8 @@ ProtocolParserProtobuf::ProcessAdditionOrRemoval(TableUpdateV4& aTableUpdate,
|
|||
break;
|
||||
|
||||
case RICE:
|
||||
// Not implemented yet (see bug 1285848),
|
||||
NS_WARNING("Encoded table update is not supported yet.");
|
||||
ret = (aIsAddition ? ProcessEncodedAddition(aTableUpdate, update)
|
||||
: ProcessEncodedRemoval(aTableUpdate, update));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -977,6 +979,132 @@ ProtocolParserProtobuf::ProcessRawRemoval(TableUpdateV4& aTableUpdate,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
DoRiceDeltaDecode(const RiceDeltaEncoding& aEncoding,
|
||||
nsTArray<uint32_t>& aDecoded)
|
||||
{
|
||||
// Sanity check of the encoding info.
|
||||
if (!aEncoding.has_first_value() ||
|
||||
!aEncoding.has_rice_parameter() ||
|
||||
!aEncoding.has_num_entries() ||
|
||||
!aEncoding.has_encoded_data()) {
|
||||
PARSER_LOG(("The encoding info is incomplete."));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PARSER_LOG(("* Encoding info:"));
|
||||
PARSER_LOG((" - First value: %d", aEncoding.first_value()));
|
||||
PARSER_LOG((" - Num of entries: %d", aEncoding.num_entries()));
|
||||
PARSER_LOG((" - Rice parameter: %d", aEncoding.rice_parameter()));
|
||||
|
||||
// Set up the input buffer. Note that the bits should be read
|
||||
// from LSB to MSB so that we in-place reverse the bits before
|
||||
// feeding to the decoder.
|
||||
auto encoded = const_cast<RiceDeltaEncoding&>(aEncoding).mutable_encoded_data();
|
||||
RiceDeltaDecoder decoder((uint8_t*)encoded->c_str(), encoded->size());
|
||||
|
||||
// Setup the output buffer. The "first value" is included in
|
||||
// the output buffer.
|
||||
aDecoded.SetLength(aEncoding.num_entries() + 1);
|
||||
aDecoded[0] = aEncoding.first_value();
|
||||
|
||||
// Decode!
|
||||
bool rv = decoder.Decode(aEncoding.rice_parameter(),
|
||||
aEncoding.first_value(), // first value.
|
||||
aEncoding.num_entries(), // # of entries (first value not included).
|
||||
&aDecoded[1]);
|
||||
|
||||
NS_ENSURE_TRUE(rv, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParserProtobuf::ProcessEncodedAddition(TableUpdateV4& aTableUpdate,
|
||||
const ThreatEntrySet& aAddition)
|
||||
{
|
||||
if (!aAddition.has_rice_hashes()) {
|
||||
PARSER_LOG(("* No rice encoded addition."));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<uint32_t> decoded;
|
||||
nsresult rv = DoRiceDeltaDecode(aAddition.rice_hashes(), decoded);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Say we have the following raw prefixes
|
||||
// BE LE
|
||||
// 00 00 00 01 1 16777216
|
||||
// 00 00 02 00 512 131072
|
||||
// 00 03 00 00 196608 768
|
||||
// 04 00 00 00 67108864 4
|
||||
//
|
||||
// which can be treated as uint32 (big-endian) sorted in increasing order:
|
||||
//
|
||||
// [1, 512, 196608, 67108864]
|
||||
//
|
||||
// According to https://developers.google.com/safe-browsing/v4/compression,
|
||||
// the following should be done prior to compression:
|
||||
//
|
||||
// 1) re-interpret in little-endian ==> [16777216, 131072, 768, 4]
|
||||
// 2) sort in increasing order ==> [4, 768, 131072, 16777216]
|
||||
//
|
||||
// In order to get the original byte stream from |decoded|
|
||||
// ([4, 768, 131072, 16777216] in this case), we have to:
|
||||
//
|
||||
// 1) sort in big-endian order ==> [16777216, 131072, 768, 4]
|
||||
// 2) copy each uint32 in little-endian to the result string
|
||||
//
|
||||
|
||||
// The 4-byte prefixes have to be re-sorted in Big-endian increasing order.
|
||||
struct CompareBigEndian
|
||||
{
|
||||
bool Equals(const uint32_t& aA, const uint32_t& aB) const
|
||||
{
|
||||
return aA == aB;
|
||||
}
|
||||
|
||||
bool LessThan(const uint32_t& aA, const uint32_t& aB) const
|
||||
{
|
||||
return NativeEndian::swapToBigEndian(aA) <
|
||||
NativeEndian::swapToBigEndian(aB);
|
||||
}
|
||||
};
|
||||
decoded.Sort(CompareBigEndian());
|
||||
|
||||
// The encoded prefixes are always 4 bytes.
|
||||
std::string prefixes;
|
||||
for (size_t i = 0; i < decoded.Length(); i++) {
|
||||
// Note that the third argument is the number of elements we want
|
||||
// to copy (and swap) but not the number of bytes we want to copy.
|
||||
char p[4];
|
||||
NativeEndian::copyAndSwapToLittleEndian(p, &decoded[i], 1);
|
||||
prefixes.append(p, 4);
|
||||
}
|
||||
|
||||
aTableUpdate.NewPrefixes(4, prefixes);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParserProtobuf::ProcessEncodedRemoval(TableUpdateV4& aTableUpdate,
|
||||
const ThreatEntrySet& aRemoval)
|
||||
{
|
||||
if (!aRemoval.has_rice_indices()) {
|
||||
PARSER_LOG(("* No rice encoded removal."));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<uint32_t> decoded;
|
||||
nsresult rv = DoRiceDeltaDecode(aRemoval.rice_indices(), decoded);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// The encoded prefixes are always 4 bytes.
|
||||
aTableUpdate.NewRemovalIndices(&decoded[0], decoded.Length());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace safebrowsing
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -184,6 +184,12 @@ private:
|
|||
|
||||
nsresult ProcessRawRemoval(TableUpdateV4& aTableUpdate,
|
||||
const ThreatEntrySet& aRemoval);
|
||||
|
||||
nsresult ProcessEncodedAddition(TableUpdateV4& aTableUpdate,
|
||||
const ThreatEntrySet& aAddition);
|
||||
|
||||
nsresult ProcessEncodedRemoval(TableUpdateV4& aTableUpdate,
|
||||
const ThreatEntrySet& aRemoval);
|
||||
};
|
||||
|
||||
} // namespace safebrowsing
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
/* 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 "RiceDeltaDecoder.h"
|
||||
|
||||
namespace {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// BitBuffer is copied and modified from webrtc/base/bitbuffer.h
|
||||
//
|
||||
|
||||
/*
|
||||
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree (webrtc/base/bitbuffer.h/cc). An additional intellectual property
|
||||
* rights grant can be found in the file PATENTS. All contributing
|
||||
* project authors may be found in the AUTHORS file in the root of
|
||||
* the source tree.
|
||||
*/
|
||||
|
||||
class BitBuffer {
|
||||
public:
|
||||
BitBuffer(const uint8_t* bytes, size_t byte_count);
|
||||
|
||||
// Gets the current offset, in bytes/bits, from the start of the buffer. The
|
||||
// bit offset is the offset into the current byte, in the range [0,7].
|
||||
void GetCurrentOffset(size_t* out_byte_offset, size_t* out_bit_offset);
|
||||
|
||||
// The remaining bits in the byte buffer.
|
||||
uint64_t RemainingBitCount() const;
|
||||
|
||||
// Reads byte-sized values from the buffer. Returns false if there isn't
|
||||
// enough data left for the specified type.
|
||||
bool ReadUInt8(uint8_t* val);
|
||||
bool ReadUInt16(uint16_t* val);
|
||||
bool ReadUInt32(uint32_t* val);
|
||||
|
||||
// Reads bit-sized values from the buffer. Returns false if there isn't enough
|
||||
// data left for the specified bit count..
|
||||
bool ReadBits(uint32_t* val, size_t bit_count);
|
||||
|
||||
// Peeks bit-sized values from the buffer. Returns false if there isn't enough
|
||||
// data left for the specified number of bits. Doesn't move the current
|
||||
// offset.
|
||||
bool PeekBits(uint32_t* val, size_t bit_count);
|
||||
|
||||
// Reads the exponential golomb encoded value at the current offset.
|
||||
// Exponential golomb values are encoded as:
|
||||
// 1) x = source val + 1
|
||||
// 2) In binary, write [countbits(x) - 1] 1s, then x
|
||||
// To decode, we count the number of leading 1 bits, read that many + 1 bits,
|
||||
// and increment the result by 1.
|
||||
// Returns false if there isn't enough data left for the specified type, or if
|
||||
// the value wouldn't fit in a uint32_t.
|
||||
bool ReadExponentialGolomb(uint32_t* val);
|
||||
// Reads signed exponential golomb values at the current offset. Signed
|
||||
// exponential golomb values are just the unsigned values mapped to the
|
||||
// sequence 0, 1, -1, 2, -2, etc. in order.
|
||||
bool ReadSignedExponentialGolomb(int32_t* val);
|
||||
|
||||
// Moves current position |byte_count| bytes forward. Returns false if
|
||||
// there aren't enough bytes left in the buffer.
|
||||
bool ConsumeBytes(size_t byte_count);
|
||||
// Moves current position |bit_count| bits forward. Returns false if
|
||||
// there aren't enough bits left in the buffer.
|
||||
bool ConsumeBits(size_t bit_count);
|
||||
|
||||
// Sets the current offset to the provied byte/bit offsets. The bit
|
||||
// offset is from the given byte, in the range [0,7].
|
||||
bool Seek(size_t byte_offset, size_t bit_offset);
|
||||
|
||||
protected:
|
||||
const uint8_t* const bytes_;
|
||||
// The total size of |bytes_|.
|
||||
size_t byte_count_;
|
||||
// The current offset, in bytes, from the start of |bytes_|.
|
||||
size_t byte_offset_;
|
||||
// The current offset, in bits, into the current byte.
|
||||
size_t bit_offset_;
|
||||
};
|
||||
|
||||
} // end of unnamed namespace
|
||||
|
||||
static void
|
||||
ReverseByte(uint8_t& b)
|
||||
{
|
||||
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
RiceDeltaDecoder::RiceDeltaDecoder(uint8_t* aEncodedData,
|
||||
size_t aEncodedDataSize)
|
||||
: mEncodedData(aEncodedData)
|
||||
, mEncodedDataSize(aEncodedDataSize)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
RiceDeltaDecoder::Decode(uint32_t aRiceParameter,
|
||||
uint32_t aFirstValue,
|
||||
uint32_t aNumEntries,
|
||||
uint32_t* aDecodedData)
|
||||
{
|
||||
// Reverse each byte before reading bits from the byte buffer.
|
||||
for (size_t i = 0; i < mEncodedDataSize; i++) {
|
||||
ReverseByte(mEncodedData[i]);
|
||||
}
|
||||
|
||||
BitBuffer bitBuffer(mEncodedData, mEncodedDataSize);
|
||||
|
||||
// q = quotient
|
||||
// r = remainder
|
||||
// k = RICE parameter
|
||||
const uint32_t k = aRiceParameter;
|
||||
uint32_t previous = aFirstValue;
|
||||
for (uint32_t i = 0; i < aNumEntries; i++) {
|
||||
// Read the quotient of N.
|
||||
uint32_t q;
|
||||
if (!bitBuffer.ReadExponentialGolomb(&q)) {
|
||||
LOG(("Encoded data underflow!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the remainder of N, one bit at a time.
|
||||
uint32_t r = 0;
|
||||
for (uint32_t j = 0; j < k; j++) {
|
||||
uint32_t b = 0;
|
||||
if (!bitBuffer.ReadBits(&b, 1)) {
|
||||
// Insufficient bits. Just leave them as zeros.
|
||||
break;
|
||||
}
|
||||
// Add the bit to the right position so that it's in Little Endian order.
|
||||
r |= b << j;
|
||||
}
|
||||
|
||||
// Caculate N from q,r,k.
|
||||
aDecodedData[i] = ((q << k) + r) + previous;
|
||||
previous = aDecodedData[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end of namespace mozilla
|
||||
} // end of namespace safebrowsing
|
||||
|
||||
namespace {
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// The BitBuffer impl is copied and modified from webrtc/base/bitbuffer.cc
|
||||
//
|
||||
|
||||
// Returns the lowest (right-most) |bit_count| bits in |byte|.
|
||||
uint8_t LowestBits(uint8_t byte, size_t bit_count) {
|
||||
return byte & ((1 << bit_count) - 1);
|
||||
}
|
||||
|
||||
// Returns the highest (left-most) |bit_count| bits in |byte|, shifted to the
|
||||
// lowest bits (to the right).
|
||||
uint8_t HighestBits(uint8_t byte, size_t bit_count) {
|
||||
MOZ_ASSERT(bit_count < 8u);
|
||||
uint8_t shift = 8 - static_cast<uint8_t>(bit_count);
|
||||
uint8_t mask = 0xFF << shift;
|
||||
return (byte & mask) >> shift;
|
||||
}
|
||||
|
||||
BitBuffer::BitBuffer(const uint8_t* bytes, size_t byte_count)
|
||||
: bytes_(bytes), byte_count_(byte_count), byte_offset_(), bit_offset_() {
|
||||
MOZ_ASSERT(static_cast<uint64_t>(byte_count_) <=
|
||||
std::numeric_limits<uint32_t>::max());
|
||||
}
|
||||
|
||||
uint64_t BitBuffer::RemainingBitCount() const {
|
||||
return (static_cast<uint64_t>(byte_count_) - byte_offset_) * 8 - bit_offset_;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadUInt8(uint8_t* val) {
|
||||
uint32_t bit_val;
|
||||
if (!ReadBits(&bit_val, sizeof(uint8_t) * 8)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(bit_val <= std::numeric_limits<uint8_t>::max());
|
||||
*val = static_cast<uint8_t>(bit_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadUInt16(uint16_t* val) {
|
||||
uint32_t bit_val;
|
||||
if (!ReadBits(&bit_val, sizeof(uint16_t) * 8)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(bit_val <= std::numeric_limits<uint16_t>::max());
|
||||
*val = static_cast<uint16_t>(bit_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadUInt32(uint32_t* val) {
|
||||
return ReadBits(val, sizeof(uint32_t) * 8);
|
||||
}
|
||||
|
||||
bool BitBuffer::PeekBits(uint32_t* val, size_t bit_count) {
|
||||
if (!val || bit_count > RemainingBitCount() || bit_count > 32) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t* bytes = bytes_ + byte_offset_;
|
||||
size_t remaining_bits_in_current_byte = 8 - bit_offset_;
|
||||
uint32_t bits = LowestBits(*bytes++, remaining_bits_in_current_byte);
|
||||
// If we're reading fewer bits than what's left in the current byte, just
|
||||
// return the portion of this byte that we need.
|
||||
if (bit_count < remaining_bits_in_current_byte) {
|
||||
*val = HighestBits(bits, bit_offset_ + bit_count);
|
||||
return true;
|
||||
}
|
||||
// Otherwise, subtract what we've read from the bit count and read as many
|
||||
// full bytes as we can into bits.
|
||||
bit_count -= remaining_bits_in_current_byte;
|
||||
while (bit_count >= 8) {
|
||||
bits = (bits << 8) | *bytes++;
|
||||
bit_count -= 8;
|
||||
}
|
||||
// Whatever we have left is smaller than a byte, so grab just the bits we need
|
||||
// and shift them into the lowest bits.
|
||||
if (bit_count > 0) {
|
||||
bits <<= bit_count;
|
||||
bits |= HighestBits(*bytes, bit_count);
|
||||
}
|
||||
*val = bits;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadBits(uint32_t* val, size_t bit_count) {
|
||||
return PeekBits(val, bit_count) && ConsumeBits(bit_count);
|
||||
}
|
||||
|
||||
bool BitBuffer::ConsumeBytes(size_t byte_count) {
|
||||
return ConsumeBits(byte_count * 8);
|
||||
}
|
||||
|
||||
bool BitBuffer::ConsumeBits(size_t bit_count) {
|
||||
if (bit_count > RemainingBitCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte_offset_ += (bit_offset_ + bit_count) / 8;
|
||||
bit_offset_ = (bit_offset_ + bit_count) % 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadExponentialGolomb(uint32_t* val) {
|
||||
if (!val) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*val = 0;
|
||||
|
||||
// Count the number of leading 0 bits by peeking/consuming them one at a time.
|
||||
size_t one_bit_count = 0;
|
||||
uint32_t peeked_bit;
|
||||
while (PeekBits(&peeked_bit, 1) && peeked_bit == 1) {
|
||||
one_bit_count++;
|
||||
ConsumeBits(1);
|
||||
}
|
||||
if (!ConsumeBits(1)) {
|
||||
return false; // The stream is incorrectly terminated at '1'.
|
||||
}
|
||||
|
||||
*val = one_bit_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitBuffer::ReadSignedExponentialGolomb(int32_t* val) {
|
||||
uint32_t unsigned_val;
|
||||
if (!ReadExponentialGolomb(&unsigned_val)) {
|
||||
return false;
|
||||
}
|
||||
if ((unsigned_val & 1) == 0) {
|
||||
*val = -static_cast<int32_t>(unsigned_val / 2);
|
||||
} else {
|
||||
*val = (unsigned_val + 1) / 2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BitBuffer::GetCurrentOffset(
|
||||
size_t* out_byte_offset, size_t* out_bit_offset) {
|
||||
MOZ_ASSERT(out_byte_offset != NULL);
|
||||
MOZ_ASSERT(out_bit_offset != NULL);
|
||||
*out_byte_offset = byte_offset_;
|
||||
*out_bit_offset = bit_offset_;
|
||||
}
|
||||
|
||||
bool BitBuffer::Seek(size_t byte_offset, size_t bit_offset) {
|
||||
if (byte_offset > byte_count_ || bit_offset > 7 ||
|
||||
(byte_offset == byte_count_ && bit_offset > 0)) {
|
||||
return false;
|
||||
}
|
||||
byte_offset_ = byte_offset;
|
||||
bit_offset_ = bit_offset;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef RICE_DELTA_DECODER_H
|
||||
#define RICE_DELTA_DECODER_H
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
class RiceDeltaDecoder {
|
||||
public:
|
||||
// This decoder is tailored for safebrowsing v4, including the
|
||||
// bit reading order and how the remainder part is interpreted.
|
||||
// The caller just needs to feed the byte stream received from
|
||||
// network directly. Note that the input buffer must be mutable
|
||||
// since the decoder will do some pre-processing before decoding.
|
||||
RiceDeltaDecoder(uint8_t* aEncodedData, size_t aEncodedDataSize);
|
||||
|
||||
bool Decode(uint32_t aRiceParameter,
|
||||
uint32_t aFirstValue,
|
||||
uint32_t aNumEntries,
|
||||
uint32_t* aDecodedData);
|
||||
|
||||
private:
|
||||
uint8_t* mEncodedData;
|
||||
size_t mEncodedDataSize;
|
||||
};
|
||||
|
||||
} // namespace safebrowsing
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // UPDATE_V4_DECODER_H
|
|
@ -31,6 +31,7 @@ UNIFIED_SOURCES += [
|
|||
'nsUrlClassifierUtils.cpp',
|
||||
'protobuf/safebrowsing.pb.cc',
|
||||
'ProtocolParser.cpp',
|
||||
'RiceDeltaDecoder.cpp',
|
||||
]
|
||||
|
||||
# define conflicting LOG() macros
|
||||
|
|
|
@ -107,10 +107,8 @@ InitListUpdateRequest(ThreatType aThreatType,
|
|||
aListUpdateRequest->set_platform_type(GetPlatformType());
|
||||
aListUpdateRequest->set_threat_entry_type(URL);
|
||||
|
||||
// Only RAW data is supported for now.
|
||||
// TODO: Bug 1285848 Supports Rice-Golomb encoding.
|
||||
Constraints* contraints = new Constraints();
|
||||
contraints->add_supported_compressions(RAW);
|
||||
contraints->add_supported_compressions(RICE);
|
||||
aListUpdateRequest->set_allocated_constraints(contraints);
|
||||
|
||||
// Only set non-empty state.
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "RiceDeltaDecoder.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::safebrowsing;
|
||||
|
||||
struct TestingData {
|
||||
std::vector<uint32_t> mExpectedDecoded;
|
||||
std::vector<uint8_t> mEncoded;
|
||||
uint32_t mRiceParameter;
|
||||
};
|
||||
|
||||
static bool runOneTest(TestingData& aData);
|
||||
|
||||
// In this batch of tests, the encoded data would be like
|
||||
// what we originally receive from the network. See comment
|
||||
// in |runOneTest| for more detail.
|
||||
TEST(RiceDeltaDecoder, Empty) {
|
||||
|
||||
// The following structure and testing data is copied from Chromium source code:
|
||||
//
|
||||
// https://chromium.googlesource.com/chromium/src.git/+/950f9975599768b6a08c7146cb4befa161be87aa/components/safe_browsing_db/v4_rice_unittest.cc#75
|
||||
//
|
||||
// and will be translated to our own testing format.
|
||||
|
||||
struct RiceDecodingTestInfo {
|
||||
uint32_t mRiceParameter;
|
||||
std::vector<uint32_t> mDeltas;
|
||||
std::string mEncoded;
|
||||
|
||||
RiceDecodingTestInfo(uint32_t aRiceParameter,
|
||||
const std::vector<uint32_t>& aDeltas,
|
||||
const std::string& aEncoded)
|
||||
: mRiceParameter(aRiceParameter)
|
||||
, mDeltas(aDeltas)
|
||||
, mEncoded(aEncoded)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the media/webrtc/trunk/webrtc/LICENSE.
|
||||
|
||||
// ----- Start of Chromium test code ----
|
||||
const std::vector<RiceDecodingTestInfo> TESTING_DATA_CHROMIUM = {
|
||||
RiceDecodingTestInfo(2, {15, 9}, "\xf7\x2"),
|
||||
RiceDecodingTestInfo(
|
||||
28, {1777762129, 2093280223, 924369848},
|
||||
"\xbf\xa8\x3f\xfb\xfc\xfb\x5e\x27\xe6\xc3\x1d\xc6\x38"),
|
||||
RiceDecodingTestInfo(
|
||||
28, {62763050, 1046523781, 192522171, 1800511020, 4442775, 582142548},
|
||||
"\x54\x60\x7b\xe7\x0a\x5f\xc1\xdc\xee\x69\xde"
|
||||
"\xfe\x58\x3c\xa3\xd6\xa5\xf2\x10\x8c\x4a\x59"
|
||||
"\x56\x00"),
|
||||
RiceDecodingTestInfo(
|
||||
28, {26067715, 344823336, 8420095, 399843890, 95029378, 731622412,
|
||||
35811335, 1047558127, 1117722715, 78698892},
|
||||
"\x06\x86\x1b\x23\x14\xcb\x46\xf2\xaf\x07\x08\xc9\x88\x54\x1f\x41\x04"
|
||||
"\xd5\x1a\x03\xeb\xe6\x3a\x80\x13\x91\x7b\xbf\x83\xf3\xb7\x85\xf1\x29"
|
||||
"\x18\xb3\x61\x09"),
|
||||
RiceDecodingTestInfo(
|
||||
27, {225846818, 328287420, 166748623, 29117720, 552397365, 350353215,
|
||||
558267528, 4738273, 567093445, 28563065, 55077698, 73091685,
|
||||
339246010, 98242620, 38060941, 63917830, 206319759, 137700744},
|
||||
"\x89\x98\xd8\x75\xbc\x44\x91\xeb\x39\x0c\x3e\x30\x9a\x78\xf3\x6a\xd4"
|
||||
"\xd9\xb1\x9f\xfb\x70\x3e\x44\x3e\xa3\x08\x67\x42\xc2\x2b\x46\x69\x8e"
|
||||
"\x3c\xeb\xd9\x10\x5a\x43\x9a\x32\xa5\x2d\x4e\x77\x0f\x87\x78\x20\xb6"
|
||||
"\xab\x71\x98\x48\x0c\x9e\x9e\xd7\x23\x0c\x13\x43\x2c\xa9\x01"),
|
||||
RiceDecodingTestInfo(
|
||||
28, {339784008, 263128563, 63871877, 69723256, 826001074, 797300228,
|
||||
671166008, 207712688},
|
||||
std::string("\x21\xc5\x02\x91\xf9\x82\xd7\x57\xb8\xe9\x3c\xf0\xc8\x4f"
|
||||
"\xe8\x64\x8d\x77\x62\x04\xd6\x85\x3f\x1c\x97\x00\x04\x1b"
|
||||
"\x17\xc6",
|
||||
30)),
|
||||
RiceDecodingTestInfo(
|
||||
28, {471820069, 196333855, 855579133, 122737976, 203433838, 85354544,
|
||||
1307949392, 165938578, 195134475, 553930435, 49231136},
|
||||
"\x95\x9c\x7d\xb0\x8f\xe8\xd9\xbd\xfe\x8c\x7f\x81\x53\x0d\x75\xdc\x4e"
|
||||
"\x40\x18\x0c\x9a\x45\x3d\xa8\xdc\xfa\x26\x59\x40\x9e\x16\x08\x43\x77"
|
||||
"\xc3\x4e\x04\x01\xa4\xe6\x5d\x00"),
|
||||
RiceDecodingTestInfo(
|
||||
27, {87336845, 129291033, 30906211, 433549264, 30899891, 53207875,
|
||||
11959529, 354827862, 82919275, 489637251, 53561020, 336722992,
|
||||
408117728, 204506246, 188216092, 9047110, 479817359, 230317256},
|
||||
"\x1a\x4f\x69\x2a\x63\x9a\xf6\xc6\x2e\xaf\x73\xd0\x6f\xd7\x31\xeb\x77"
|
||||
"\x1d\x43\xe3\x2b\x93\xce\x67\x8b\x59\xf9\x98\xd4\xda\x4f\x3c\x6f\xb0"
|
||||
"\xe8\xa5\x78\x8d\x62\x36\x18\xfe\x08\x1e\x78\xd8\x14\x32\x24\x84\x61"
|
||||
"\x1c\xf3\x37\x63\xc4\xa0\x88\x7b\x74\xcb\x64\xc8\x5c\xba\x05"),
|
||||
RiceDecodingTestInfo(
|
||||
28, {297968956, 19709657, 259702329, 76998112, 1023176123, 29296013,
|
||||
1602741145, 393745181, 177326295, 55225536, 75194472},
|
||||
"\xf1\x94\x0a\x87\x6c\x5f\x96\x90\xe3\xab\xf7\xc0\xcb\x2d\xe9\x76\xdb"
|
||||
"\xf8\x59\x63\xc1\x6f\x7c\x99\xe3\x87\x5f\xc7\x04\xde\xb9\x46\x8e\x54"
|
||||
"\xc0\xac\x4a\x03\x0d\x6c\x8f\x00"),
|
||||
RiceDecodingTestInfo(
|
||||
28, {532220688, 780594691, 436816483, 163436269, 573044456, 1069604,
|
||||
39629436, 211410997, 227714491, 381562898, 75610008, 196754597,
|
||||
40310339, 15204118, 99010842},
|
||||
"\x41\x2c\xe4\xfe\x06\xdc\x0d\xbd\x31\xa5\x04\xd5\x6e\xdd\x9b\x43\xb7"
|
||||
"\x3f\x11\x24\x52\x10\x80\x4f\x96\x4b\xd4\x80\x67\xb2\xdd\x52\xc9\x4e"
|
||||
"\x02\xc6\xd7\x60\xde\x06\x92\x52\x1e\xdd\x35\x64\x71\x26\x2c\xfe\xcf"
|
||||
"\x81\x46\xb2\x79\x01"),
|
||||
RiceDecodingTestInfo(
|
||||
28, {219354713, 389598618, 750263679, 554684211, 87381124, 4523497,
|
||||
287633354, 801308671, 424169435, 372520475, 277287849},
|
||||
"\xb2\x2c\x26\x3a\xcd\x66\x9c\xdb\x5f\x07\x2e\x6f\xe6\xf9\x21\x10\x52"
|
||||
"\xd5\x94\xf4\x82\x22\x48\xf9\x9d\x24\xf6\xff\x2f\xfc\x6d\x3f\x21\x65"
|
||||
"\x1b\x36\x34\x56\xea\xc4\x21\x00"),
|
||||
};
|
||||
|
||||
// ----- End of Chromium test code ----
|
||||
|
||||
for (auto tdc : TESTING_DATA_CHROMIUM) {
|
||||
// Populate chromium testing data to our native testing data struct.
|
||||
TestingData d;
|
||||
|
||||
d.mRiceParameter = tdc.mRiceParameter; // Populate rice parameter.
|
||||
|
||||
// Populate encoded data from std::string to vector<uint8>.
|
||||
d.mEncoded.resize(tdc.mEncoded.size());
|
||||
memcpy(&d.mEncoded[0], tdc.mEncoded.c_str(), tdc.mEncoded.size());
|
||||
|
||||
// Populate deltas to expected decoded data. The first value would be just
|
||||
// set to an arbitrary value, say 7, to avoid any assumption to the
|
||||
// first value in the implementation.
|
||||
d.mExpectedDecoded.resize(tdc.mDeltas.size() + 1);
|
||||
for (size_t i = 0; i < d.mExpectedDecoded.size(); i++) {
|
||||
if (0 == i) {
|
||||
d.mExpectedDecoded[i] = 7; // "7" is an arbitrary starting value
|
||||
} else {
|
||||
d.mExpectedDecoded[i] = d.mExpectedDecoded[i - 1] + tdc.mDeltas[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_TRUE(runOneTest(d));
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
runOneTest(TestingData& aData)
|
||||
{
|
||||
RiceDeltaDecoder decoder(&aData.mEncoded[0], aData.mEncoded.size());
|
||||
|
||||
std::vector<uint32_t> decoded(aData.mExpectedDecoded.size());
|
||||
|
||||
decoded[0] = aData.mExpectedDecoded[0];
|
||||
bool rv = decoder.Decode(aData.mRiceParameter,
|
||||
decoded[0], // first value.
|
||||
decoded.size() - 1, // # of entries (first value not included).
|
||||
&decoded[1]);
|
||||
|
||||
return rv && decoded == aData.mExpectedDecoded;
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче