This commit is contained in:
Ryan VanderMeulen 2016-10-07 09:46:31 -04:00
Родитель a1439d9983 7ebb50e21d
Коммит c4df0e3dd3
118 изменённых файлов: 4835 добавлений и 818 удалений

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

@ -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 &lt;input type='time'&gt;</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 &lt;input type='time'&gt;</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 servers 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 servers 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;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше