This commit is contained in:
Wes Kocher 2016-11-23 16:18:24 -08:00
Родитель 896b2dc9c8 b4e6fa3527
Коммит b851c06cc9
169 изменённых файлов: 2285 добавлений и 2052 удалений

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

@ -73,7 +73,9 @@ EMBED_MANIFEST_AT = 2
INSTALL_TARGETS += midl
midl_FILES := $(filter %.h %_i.c,$(MIDL_GENERATED_FILES))
midl_DEST = $(DIST)/include
midl_TARGET := export
midl_TARGET := midl
export:: midl
include $(topsrcdir)/config/rules.mk

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

@ -47,4 +47,6 @@ midl_exports := \
INSTALL_TARGETS += midl_exports
midl_exports_FILES := $(midl_exports)
midl_exports_DEST = $(DIST)/include
midl_exports_TARGET := export
midl_exports_TARGET := midl
export:: midl

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

@ -223,6 +223,6 @@ def gen_files(fd, conf_file, xpidllex, xpidlyacc):
deps = set()
conf, inc_dir = get_conf(conf_file)
deps.update(print_header_file(fd, conf, inc_dir))
with open('xpcAccEvents.cpp', 'w') as cpp_fd:
with open(os.path.join(os.path.dirname(fd.name), 'xpcAccEvents.cpp'), 'w') as cpp_fd:
deps.update(print_cpp_file(cpp_fd, conf, inc_dir))
return deps

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

@ -90,5 +90,5 @@ if CONFIG['MOZ_LINUX_32_SSE2_STARTUP_ERROR']:
DEFINES['MOZ_LINUX_32_SSE2_STARTUP_ERROR'] = True
for icon in ('firefox', 'document', 'newwindow', 'newtab', 'pbmode'):
DEFINES[icon.upper() + '_ICO'] = '"%s/dist/branding/%s.ico"' % (
TOPOBJDIR, icon)
DEFINES[icon.upper() + '_ICO'] = '"%s/%s/%s.ico"' % (
TOPSRCDIR, CONFIG['MOZ_BRANDING_DIRECTORY'], icon)

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

@ -924,9 +924,6 @@ extensions.on("shutdown", (type, extension) => {
// Manages mapping between XUL windows and extension window IDs.
global.WindowManager = {
_windows: new WeakMap(),
_nextId: 0,
// Note: These must match the values in windows.json.
WINDOW_ID_NONE: -1,
WINDOW_ID_CURRENT: -2,
@ -965,12 +962,11 @@ global.WindowManager = {
},
getId(window) {
if (this._windows.has(window)) {
return this._windows.get(window);
if (!window.QueryInterface) {
return null;
}
let id = this._nextId++;
this._windows.set(window, id);
return id;
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
},
getWindow(id, context) {

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

@ -261,7 +261,7 @@ this.MigratorPrototype = {
for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
let histogramId =
"FX_MIGRATION_" + resourceType.toUpperCase() + "_QUANTITY";
let histogram = Services.telemetry.getKeyedHistogram(histogramId);
let histogram = Services.telemetry.getKeyedHistogramById(histogramId);
histogram.add(this.getKey(), MigrationUtils._importQuantities[resourceType]);
}
} catch (ex) { /* Telemetry is exception-happy */ }

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

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

@ -18,24 +18,7 @@ else:
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
DIRS += ['annotationProcessors']
for var in ('GRE_MILESTONE', 'MOZ_APP_VERSION', 'MOZ_APP_BASENAME',
'MOZ_APP_VENDOR', 'MOZ_APP_ID', 'MAR_CHANNEL_ID',
'ACCEPTED_MAR_CHANNEL_IDS', 'MOZ_APP_REMOTINGNAME'):
DEFINES[var] = CONFIG[var]
if CONFIG['MOZ_APP_DISPLAYNAME'] != CONFIG['MOZ_APP_BASENAME']:
DEFINES['MOZ_APP_DISPLAYNAME'] = CONFIG['MOZ_APP_DISPLAYNAME']
if CONFIG['MOZ_BUILD_APP'] == 'browser':
DEFINES['MOZ_BUILD_APP_IS_BROWSER'] = True
if CONFIG['MOZ_APP_PROFILE']:
DEFINES['MOZ_APP_PROFILE'] = CONFIG['MOZ_APP_PROFILE']
for var in ('MOZ_CRASHREPORTER', 'MOZ_PROFILE_MIGRATOR',
'MOZ_APP_STATIC_INI'):
if CONFIG[var]:
DEFINES[var] = True
DEFINES['ACCEPTED_MAR_CHANNEL_IDS'] = CONFIG['ACCEPTED_MAR_CHANNEL_IDS']
if CONFIG['MOZ_BUILD_APP'] == 'browser':
PYTHON_UNITTEST_MANIFESTS += [
@ -63,7 +46,37 @@ if CONFIG['MOZ_ASAN'] and CONFIG['CLANG_CL']:
FINAL_TARGET_FILES += ['%' + CONFIG['MOZ_CLANG_RT_ASAN_LIB_PATH']]
if CONFIG['MOZ_APP_BASENAME']:
FINAL_TARGET_PP_FILES += ['application.ini']
appini_defines = {
'TOPOBJDIR': TOPOBJDIR,
}
for var in ('GRE_MILESTONE', 'MOZ_APP_VERSION', 'MOZ_APP_BASENAME',
'MOZ_APP_VENDOR', 'MOZ_APP_ID', 'MAR_CHANNEL_ID',
'MOZ_APP_REMOTINGNAME'):
appini_defines[var] = CONFIG[var]
if CONFIG['MOZ_APP_DISPLAYNAME'] != CONFIG['MOZ_APP_BASENAME']:
appini_defines['MOZ_APP_DISPLAYNAME'] = CONFIG['MOZ_APP_DISPLAYNAME']
if CONFIG['MOZ_BUILD_APP'] == 'browser':
appini_defines['MOZ_BUILD_APP_IS_BROWSER'] = True
if CONFIG['MOZ_APP_PROFILE']:
appini_defines['MOZ_APP_PROFILE'] = CONFIG['MOZ_APP_PROFILE']
for var in ('MOZ_CRASHREPORTER', 'MOZ_PROFILE_MIGRATOR',
'MOZ_APP_STATIC_INI'):
if CONFIG[var]:
appini_defines[var] = True
GENERATED_FILES += ['application.ini']
appini = GENERATED_FILES['application.ini']
appini.script = '../python/mozbuild/mozbuild/action/preprocessor.py:generate'
appini.inputs = ['application.ini.in']
appini.flags = ['-D%s=%s' % (k, '1' if v is True else v)
for k, v in appini_defines.iteritems()]
FINAL_TARGET_FILES += ['!application.ini']
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' and CONFIG['MOZ_UPDATER']:
FINAL_TARGET_PP_FILES += ['update-settings.ini']
@ -71,9 +84,7 @@ if CONFIG['MOZ_APP_BASENAME']:
GENERATED_FILES += ['application.ini.h']
appini = GENERATED_FILES['application.ini.h']
appini.script = 'appini_header.py'
appini.inputs = ['!/dist/bin/application.ini']
DEFINES['TOPOBJDIR'] = TOPOBJDIR
appini.inputs = ['!application.ini']
# NOTE: Keep .gdbinit in the topsrcdir for people who run gdb from the topsrcdir.
OBJDIR_FILES += ['/.gdbinit']

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

@ -108,3 +108,5 @@ endif
# It also requires all the install manifests for dist/bin to have been processed
# because it adds interfaces.manifest references with buildlist.py.
$(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(addprefix install-,$(filter dist/bin%,$(INSTALL_MANIFESTS)))
$(TOPOBJDIR)/build/application.ini: $(TOPOBJDIR)/buildid.h $(TOPOBJDIR)/source-repo.h

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

@ -35,6 +35,7 @@ tree.labels.Cache=Cache Storage
# LOCALIZATION NOTE (table.headers.*.*):
# These strings are the header names of the columns in the Storage Table for
# each type of storage available through the Storage Tree to the side.
table.headers.cookies.uniqueKey=Unique key
table.headers.cookies.name=Name
table.headers.cookies.path=Path
table.headers.cookies.host=Domain

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

@ -140,7 +140,6 @@ skip-if = true # Bug 1258809
skip-if = true # Bug 1258809
[browser_net_simple-request.js]
[browser_net_sort-01.js]
skip-if = (e10s && debug && os == 'mac') # Bug 1253037
[browser_net_sort-02.js]
[browser_net_sort-03.js]
[browser_net_statistics-01.js]

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

@ -615,8 +615,13 @@ TableWidget.prototype = {
/**
* Populates the header context menu with the names of the columns along with
* displaying which columns are hidden or visible.
*
* @param {Array} privateColumns=[]
* An array of column names that should never appear in the table. This
* allows us to e.g. have an invisible compound primary key for a
* table's rows.
*/
populateMenuPopup: function () {
populateMenuPopup: function (privateColumns = []) {
if (!this.menupopup) {
return;
}
@ -626,6 +631,10 @@ TableWidget.prototype = {
}
for (let column of this.columns.values()) {
if (privateColumns.includes(column.id)) {
continue;
}
let menuitem = this.document.createElementNS(XUL_NS, "menuitem");
menuitem.setAttribute("label", column.header.getAttribute("value"));
menuitem.setAttribute("data-id", column.id);
@ -663,16 +672,21 @@ TableWidget.prototype = {
* Creates the columns in the table. Without calling this method, data cannot
* be inserted into the table unless `initialColumns` was supplied.
*
* @param {object} columns
* @param {Object} columns
* A key value pair representing the columns of the table. Where the
* key represents the id of the column and the value is the displayed
* label in the header of the column.
* @param {string} sortOn
* @param {String} sortOn
* The id of the column on which the table will be initially sorted on.
* @param {array} hiddenColumns
* @param {Array} hiddenColumns
* Ids of all the columns that are hidden by default.
* @param {Array} privateColumns=[]
* An array of column names that should never appear in the table. This
* allows us to e.g. have an invisible compound primary key for a
* table's rows.
*/
setColumns: function (columns, sortOn = this.sortedOn, hiddenColumns = []) {
setColumns: function (columns, sortOn = this.sortedOn, hiddenColumns = [],
privateColumns = []) {
for (let column of this.columns.values()) {
column.destroy();
}
@ -702,13 +716,14 @@ TableWidget.prototype = {
}
this.columns.set(id, new Column(this, id, columns[id]));
if (hiddenColumns.indexOf(id) > -1) {
if (hiddenColumns.includes(id) || privateColumns.includes(id)) {
// Hide the column.
this.columns.get(id).toggleColumn();
}
}
this.sortedOn = sortOn;
this.sortBy(this.sortedOn);
this.populateMenuPopup();
this.populateMenuPopup(privateColumns);
},
/**
@ -778,6 +793,11 @@ TableWidget.prototype = {
return;
}
if (this.editBookmark && !this.items.has(this.editBookmark)) {
// Key has been updated... update bookmark.
this.editBookmark = item[this.uniqueId];
}
let index = this.columns.get(this.sortedOn).push(item);
for (let [key, column] of this.columns) {
if (key != this.sortedOn) {

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

@ -29,7 +29,9 @@ support-files =
[browser_storage_delete.js]
[browser_storage_delete_all.js]
[browser_storage_delete_tree.js]
[browser_storage_dynamic_updates.js]
[browser_storage_dynamic_updates_cookies.js]
[browser_storage_dynamic_updates_localStorage.js]
[browser_storage_dynamic_updates_sessionStorage.js]
[browser_storage_empty_objectstores.js]
[browser_storage_indexeddb_delete.js]
[browser_storage_indexeddb_delete_blocked.js]

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

@ -2,6 +2,8 @@
* 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/. */
/* import-globals-from head.js */
// Basic test to assert that the storage tree and table corresponding to each
// item in the storage tree is correctly displayed
@ -21,10 +23,23 @@
"use strict";
const testCases = [
[["cookies", "test1.example.org"],
["c1", "cs2", "c3", "uc1"]],
[["cookies", "sectest1.example.org"],
["uc1", "cs2", "sc1"]],
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("cs2", ".example.org", "/"),
getCookieId("c3", "test1.example.org", "/"),
getCookieId("uc1", ".example.org", "/")
]
],
[
["cookies", "sectest1.example.org"],
[
getCookieId("uc1", ".example.org", "/"),
getCookieId("cs2", ".example.org", "/"),
getCookieId("sc1", "sectest1.example.org", "/browser/devtools/client/storage/test/")
]
],
[["localStorage", "http://test1.example.org"],
["ls1", "ls2"]],
[["localStorage", "http://sectest1.example.org"],

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

@ -21,8 +21,8 @@ function* performDelete(store, rowName, deleteAll) {
yield selectTreeItem(store);
let eventWait = gUI.once("store-objects-updated");
let cells = getRowCells(rowName, true);
let cells = getRowCells(rowName);
yield waitForContextMenu(contextMenu, cells.name, () => {
info(`Opened context menu in ${storeName}, row '${rowName}'`);
if (deleteAll) {
@ -43,24 +43,54 @@ add_task(function* () {
info("test state before delete");
yield checkState([
[["cookies", "test1.example.org"], ["c1", "c3", "cs2", "uc1"]],
[["cookies", "sectest1.example.org"], ["cs2", "sc1", "uc1"]],
[
["cookies", "test1.example.org"], [
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c3", "test1.example.org", "/"),
getCookieId("cs2", ".example.org", "/"),
getCookieId("uc1", ".example.org", "/")
]
],
[
["cookies", "sectest1.example.org"], [
getCookieId("cs2", ".example.org", "/"),
getCookieId("sc1", "sectest1.example.org",
"/browser/devtools/client/storage/test/"),
getCookieId("uc1", ".example.org", "/")
]
],
]);
info("delete all from domain");
// delete only cookies that match the host exactly
yield performDelete(["cookies", "test1.example.org"], "c1", false);
let id = getCookieId("c1", "test1.example.org", "/browser");
yield performDelete(["cookies", "test1.example.org"], id, false);
info("test state after delete all from domain");
yield checkState([
// Domain cookies (.example.org) must not be deleted.
[["cookies", "test1.example.org"], ["cs2", "uc1"]],
[["cookies", "sectest1.example.org"], ["cs2", "sc1", "uc1"]],
[
["cookies", "test1.example.org"],
[
getCookieId("cs2", ".example.org", "/"),
getCookieId("uc1", ".example.org", "/")
]
],
[
["cookies", "sectest1.example.org"],
[
getCookieId("cs2", ".example.org", "/"),
getCookieId("uc1", ".example.org", "/"),
getCookieId("sc1", "sectest1.example.org",
"/browser/devtools/client/storage/test/"),
]
],
]);
info("delete all");
// delete all cookies for host, including domain cookies
yield performDelete(["cookies", "sectest1.example.org"], "uc1", true);
id = getCookieId("uc1", ".example.org", "/");
yield performDelete(["cookies", "sectest1.example.org"], id, true);
info("test state after delete all");
yield checkState([

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

@ -13,8 +13,16 @@ add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
yield checkState([
[["cookies", "test1.example.org"],
["test1", "test2", "test3", "test4", "test5"]],
[
["cookies", "test1.example.org"],
[
getCookieId("test1", ".test1.example.org", "/browser"),
getCookieId("test2", "test1.example.org", "/browser"),
getCookieId("test3", ".test1.example.org", "/browser"),
getCookieId("test4", "test1.example.org", "/browser"),
getCookieId("test5", ".test1.example.org", "/browser")
]
],
]);
yield finishTests();

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

@ -10,13 +10,20 @@ add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
showAllColumns(true);
yield editCell("test3", "name", "newTest3");
yield editCell("newTest3", "path", "/");
yield editCell("newTest3", "host", "test1.example.org");
yield editCell("newTest3", "expires", "Tue, 14 Feb 2040 17:41:14 GMT");
yield editCell("newTest3", "value", "newValue3");
yield editCell("newTest3", "isSecure", "true");
yield editCell("newTest3", "isHttpOnly", "true");
let id = getCookieId("test3", ".test1.example.org", "/browser");
yield editCell(id, "name", "newTest3");
id = getCookieId("newTest3", ".test1.example.org", "/browser");
yield editCell(id, "host", "test1.example.org");
id = getCookieId("newTest3", "test1.example.org", "/browser");
yield editCell(id, "path", "/");
id = getCookieId("newTest3", "test1.example.org", "/");
yield editCell(id, "expires", "Tue, 14 Feb 2040 17:41:14 GMT");
yield editCell(id, "value", "newValue3");
yield editCell(id, "isSecure", "true");
yield editCell(id, "isHttpOnly", "true");
yield finishTests();
});

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

@ -10,10 +10,11 @@ add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
showAllColumns(true);
yield startCellEdit("test4", "name");
let id = getCookieId("test4", "test1.example.org", "/browser");
yield startCellEdit(id, "name");
yield typeWithTerminator("test6", "VK_TAB");
yield typeWithTerminator("/", "VK_TAB");
yield typeWithTerminator(".example.org", "VK_TAB");
yield typeWithTerminator("/", "VK_TAB");
yield typeWithTerminator("Tue, 25 Dec 2040 12:00:00 GMT", "VK_TAB");
yield typeWithTerminator("test6value", "VK_TAB");
yield typeWithTerminator("false", "VK_TAB");

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

@ -10,7 +10,8 @@ add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
showAllColumns(true);
yield startCellEdit("test1", "name");
let id = getCookieId("test1", ".test1.example.org", "/browser");
yield startCellEdit(id, "name");
PressKeyXTimes("VK_TAB", 18);
is(getCurrentEditorValue(), "value3",

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

@ -13,8 +13,10 @@ const TEST_CASES = [
"ls1", "name"],
[["sessionStorage", "http://test1.example.org"],
"ss1", "name"],
[["cookies", "test1.example.org"],
"c1", "name"],
[
["cookies", "test1.example.org"],
getCookieId("c1", "test1.example.org", "/browser"), "name"
],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
1, "name"],
[["Cache", "http://test1.example.org", "plop"],
@ -41,7 +43,7 @@ add_task(function* () {
yield waitForContextMenu(contextMenu, row[cellToClick], () => {
info(`Opened context menu in ${treeItemName}, row '${rowName}'`);
menuDeleteItem.click();
let truncatedRowName = String(rowName).substr(0, 16);
let truncatedRowName = String(rowName).replace(SEPARATOR_GUID, "-").substr(0, 16);
ok(menuDeleteItem.getAttribute("label").includes(truncatedRowName),
`Context menu item label contains '${rowName}' (maybe truncated)`);
});

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

@ -17,7 +17,15 @@ add_task(function* () {
info("test state before delete");
yield checkState([
[["cookies", "test1.example.org"], ["c1", "c3", "cs2", "uc1"]],
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("cs2", ".example.org", "/"),
getCookieId("c3", "test1.example.org", "/"),
getCookieId("uc1", ".example.org", "/")
]
],
[["localStorage", "http://test1.example.org"], ["ls1", "ls2"]],
[["sessionStorage", "http://test1.example.org"], ["ss1"]],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], [1, 2, 3]],

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

@ -1,213 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
let $ = id => gPanelWindow.document.querySelector(id);
let $$ = sel => gPanelWindow.document.querySelectorAll(sel);
gUI.tree.expandAll();
ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
yield selectTableItem("c1");
// test that value is something initially
let initialValue = [[
{name: "c1", value: "1.2.3.4.5.6.7"},
{name: "c1.Path", value: "/browser"}
], [
{name: "c1", value: "Array"},
{name: "c1.0", value: "1"},
{name: "c1.6", value: "7"}
]];
// test that value is something initially
let finalValue = [[
{name: "c1", value: '{"foo": 4,"bar":6}'},
{name: "c1.Path", value: "/browser"}
], [
{name: "c1", value: "Object"},
{name: "c1.foo", value: "4"},
{name: "c1.bar", value: "6"}
]];
// Check that sidebar shows correct initial value
yield findVariableViewProperties(initialValue[0], false);
yield findVariableViewProperties(initialValue[1], true);
// Check if table shows correct initial value
ok($("#value [data-id='c1'].table-widget-cell"), "cell is present");
is($("#value [data-id='c1'].table-widget-cell").value, "1.2.3.4.5.6.7",
"correct initial value in table");
gWindow.addCookie("c1", '{"foo": 4,"bar":6}', "/browser");
yield gUI.once("sidebar-updated");
yield findVariableViewProperties(finalValue[0], false);
yield findVariableViewProperties(finalValue[1], true);
ok($("#value [data-id='c1'].table-widget-cell"),
"cell is present after update");
is($("#value [data-id='c1'].table-widget-cell").value, '{"foo": 4,"bar":6}',
"correct final value in table");
// Add a new entry
is($$("#value .table-widget-cell").length, 2,
"Correct number of rows before update 0");
gWindow.addCookie("c3", "booyeah");
// Wait once for update and another time for value fetching
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
is($$("#value .table-widget-cell").length, 3,
"Correct number of rows after update 1");
// Add another
gWindow.addCookie("c4", "booyeah");
// Wait once for update and another time for value fetching
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
is($$("#value .table-widget-cell").length, 4,
"Correct number of rows after update 2");
// Removing cookies
gWindow.removeCookie("c1", "/browser");
yield gUI.once("sidebar-updated");
is($$("#value .table-widget-cell").length, 3,
"Correct number of rows after delete update 3");
ok(!$("#c1"), "Correct row got deleted");
ok(!gUI.sidebar.hidden, "Sidebar still visible for next row");
// Check if next element's value is visible in sidebar
yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
// Keep deleting till no rows
gWindow.removeCookie("c3");
yield gUI.once("store-objects-updated");
is($$("#value .table-widget-cell").length, 2,
"Correct number of rows after delete update 4");
// Check if next element's value is visible in sidebar
yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
gWindow.removeCookie("c2", "/browser");
yield gUI.once("sidebar-updated");
yield findVariableViewProperties([{name: "c4", value: "booyeah"}]);
is($$("#value .table-widget-cell").length, 1,
"Correct number of rows after delete update 5");
gWindow.removeCookie("c4");
yield gUI.once("store-objects-updated");
is($$("#value .table-widget-cell").length, 0,
"Correct number of rows after delete update 6");
ok(gUI.sidebar.hidden, "Sidebar is hidden when no rows");
// Testing in local storage
yield selectTreeItem(["localStorage", "http://test1.example.org"]);
is($$("#value .table-widget-cell").length, 7,
"Correct number of rows after delete update 7");
ok($(".table-widget-cell[data-id='ls4']"), "ls4 exists before deleting");
gWindow.localStorage.removeItem("ls4");
yield gUI.once("store-objects-updated");
is($$("#value .table-widget-cell").length, 6,
"Correct number of rows after delete update 8");
ok(!$(".table-widget-cell[data-id='ls4']"),
"ls4 does not exists after deleting");
gWindow.localStorage.setItem("ls4", "again");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
is($$("#value .table-widget-cell").length, 7,
"Correct number of rows after delete update 9");
ok($(".table-widget-cell[data-id='ls4']"),
"ls4 came back after adding it again");
// Updating a row
gWindow.localStorage.setItem("ls2", "ls2-changed");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
is($("#value [data-id='ls2']").value, "ls2-changed",
"Value got updated for local storage");
// Testing in session storage
yield selectTreeItem(["sessionStorage", "http://test1.example.org"]);
is($$("#value .table-widget-cell").length, 3,
"Correct number of rows for session storage");
gWindow.sessionStorage.setItem("ss4", "new-item");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
is($$("#value .table-widget-cell").length, 4,
"Correct number of rows after session storage update");
// deleting item
gWindow.sessionStorage.removeItem("ss3");
yield gUI.once("store-objects-updated");
gWindow.sessionStorage.removeItem("ss1");
yield gUI.once("store-objects-updated");
is($$("#value .table-widget-cell").length, 2,
"Correct number of rows after removing items from session storage");
yield selectTableItem("ss2");
ok(!gUI.sidebar.hidden, "sidebar is visible");
// Checking for correct value in sidebar before update
yield findVariableViewProperties([{name: "ss2", value: "foobar"}]);
gWindow.sessionStorage.setItem("ss2", "changed=ss2");
yield gUI.once("sidebar-updated");
is($("#value [data-id='ss2']").value, "changed=ss2",
"Value got updated for session storage in the table");
yield findVariableViewProperties([{name: "ss2", value: "changed=ss2"}]);
// Clearing items. Bug 1233497 makes it so that we can no longer yield
// CPOWs from Tasks. We work around this by calling clear via a ContentTask
// instead.
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
return Task.spawn(content.wrappedJSObject.clear);
});
yield gUI.once("store-objects-cleared");
is($$("#value .table-widget-cell").length, 0,
"Table should be cleared");
yield finishTests();
});

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

@ -0,0 +1,188 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Test dynamic updates in the storage inspector for cookies.
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
gUI.tree.expandAll();
ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
let c1id = getCookieId("c1", "test1.example.org", "/browser");
yield selectTableItem(c1id);
// test that value is something initially
let initialValue = [[
{name: "c1", value: "1.2.3.4.5.6.7"},
{name: "c1.Path", value: "/browser"}
], [
{name: "c1", value: "Array"},
{name: "c1.0", value: "1"},
{name: "c1.6", value: "7"}
]];
// test that value is something initially
let finalValue = [[
{name: "c1", value: '{"foo": 4,"bar":6}'},
{name: "c1.Path", value: "/browser"}
], [
{name: "c1", value: "Object"},
{name: "c1.foo", value: "4"},
{name: "c1.bar", value: "6"}
]];
// Check that sidebar shows correct initial value
yield findVariableViewProperties(initialValue[0], false);
yield findVariableViewProperties(initialValue[1], true);
// Check if table shows correct initial value
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c2", "test1.example.org", "/browser")
]
],
]);
checkCell(c1id, "value", "1.2.3.4.5.6.7");
gWindow.addCookie("c1", '{"foo": 4,"bar":6}', "/browser");
yield gUI.once("sidebar-updated");
yield findVariableViewProperties(finalValue[0], false);
yield findVariableViewProperties(finalValue[1], true);
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c2", "test1.example.org", "/browser")
]
],
]);
checkCell(c1id, "value", '{"foo": 4,"bar":6}');
// Add a new entry
gWindow.addCookie("c3", "booyeah");
// Wait once for update and another time for value fetching
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c2", "test1.example.org", "/browser"),
getCookieId("c3", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
let c3id = getCookieId("c3", "test1.example.org",
"/browser/devtools/client/storage/test/");
checkCell(c3id, "value", "booyeah");
// Add another
gWindow.addCookie("c4", "booyeah");
// Wait once for update and another time for value fetching
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c2", "test1.example.org", "/browser"),
getCookieId("c3", "test1.example.org",
"/browser/devtools/client/storage/test/"),
getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
let c4id = getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/");
checkCell(c4id, "value", "booyeah");
// Removing cookies
gWindow.removeCookie("c1", "/browser");
yield gUI.once("sidebar-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c2", "test1.example.org", "/browser"),
getCookieId("c3", "test1.example.org",
"/browser/devtools/client/storage/test/"),
getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
ok(!gUI.sidebar.hidden, "Sidebar still visible for next row");
// Check if next element's value is visible in sidebar
yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
// Keep deleting till no rows
gWindow.removeCookie("c3");
yield gUI.once("store-objects-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c2", "test1.example.org", "/browser"),
getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
// Check if next element's value is visible in sidebar
yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
gWindow.removeCookie("c2", "/browser");
yield gUI.once("sidebar-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
// Check if next element's value is visible in sidebar
yield findVariableViewProperties([{name: "c4", value: "booyeah"}]);
gWindow.removeCookie("c4");
yield gUI.once("store-objects-updated");
yield checkState([
[["cookies", "test1.example.org"], [ ]],
]);
ok(gUI.sidebar.hidden, "Sidebar is hidden when no rows");
yield finishTests();
});

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

@ -0,0 +1,70 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Test dynamic updates in the storage inspector for localStorage.
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
gUI.tree.expandAll();
ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
yield checkState([
[
["localStorage", "http://test1.example.org"],
["ls1", "ls2", "ls3", "ls4", "ls5", "ls6", "ls7"]
],
]);
gWindow.localStorage.removeItem("ls4");
yield gUI.once("store-objects-updated");
yield checkState([
[
["localStorage", "http://test1.example.org"],
["ls1", "ls2", "ls3", "ls5", "ls6", "ls7"]
],
]);
gWindow.localStorage.setItem("ls4", "again");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
yield checkState([
[
["localStorage", "http://test1.example.org"],
["ls1", "ls2", "ls3", "ls4", "ls5", "ls6", "ls7"]
],
]);
// Updating a row
gWindow.localStorage.setItem("ls2", "ls2-changed");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
checkCell("ls2", "value", "ls2-changed");
// Clearing items. Bug 1233497 makes it so that we can no longer yield
// CPOWs from Tasks. We work around this by calling clear via a ContentTask
// instead.
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
return Task.spawn(content.wrappedJSObject.clear);
});
yield gUI.once("store-objects-cleared");
yield checkState([
[
["localStorage", "http://test1.example.org"],
[ ]
],
]);
yield finishTests();
});

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

@ -0,0 +1,84 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Test dynamic updates in the storage inspector for sessionStorage.
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
gUI.tree.expandAll();
ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
yield checkState([
[
["sessionStorage", "http://test1.example.org"],
["ss1", "ss2", "ss3"]
],
]);
gWindow.sessionStorage.setItem("ss4", "new-item");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
yield checkState([
[
["sessionStorage", "http://test1.example.org"],
["ss1", "ss2", "ss3", "ss4"]
],
]);
// deleting item
gWindow.sessionStorage.removeItem("ss3");
yield gUI.once("store-objects-updated");
gWindow.sessionStorage.removeItem("ss1");
yield gUI.once("store-objects-updated");
yield checkState([
[
["sessionStorage", "http://test1.example.org"],
["ss2", "ss4"]
],
]);
yield selectTableItem("ss2");
ok(!gUI.sidebar.hidden, "sidebar is visible");
// Checking for correct value in sidebar before update
yield findVariableViewProperties([{name: "ss2", value: "foobar"}]);
gWindow.sessionStorage.setItem("ss2", "changed=ss2");
yield gUI.once("sidebar-updated");
checkCell("ss2", "value", "changed=ss2");
yield findVariableViewProperties([{name: "ss2", value: "changed=ss2"}]);
// Clearing items. Bug 1233497 makes it so that we can no longer yield
// CPOWs from Tasks. We work around this by calling clear via a ContentTask
// instead.
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
return Task.spawn(content.wrappedJSObject.clear);
});
yield gUI.once("store-objects-cleared");
yield checkState([
[
["sessionStorage", "http://test1.example.org"],
[ ]
],
]);
yield finishTests();
});

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

@ -20,22 +20,22 @@ const testCases = [
sidebarHidden: true
},
{
location: "cs2",
location: getCookieId("cs2", ".example.org", "/"),
sidebarHidden: false
},
{
sendEscape: true
},
{
location: "cs2",
location: getCookieId("cs2", ".example.org", "/"),
sidebarHidden: false
},
{
location: "uc1",
location: getCookieId("uc1", ".example.org", "/"),
sidebarHidden: false
},
{
location: "uc1",
location: getCookieId("uc1", ".example.org", "/"),
sidebarHidden: false
},

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

@ -17,7 +17,7 @@
const LONG_WORD = "a".repeat(1000);
const testCases = [
["cs2", [
[getCookieId("cs2", ".example.org", "/"), [
{name: "cs2", value: "sessionCookie"},
{name: "cs2.Path", value: "/"},
{name: "cs2.HostOnly", value: "false"},
@ -26,7 +26,7 @@ const testCases = [
{name: "cs2.Expires", value: "Session"},
{name: "cs2.Secure", value: "false"},
]],
["c1", [
[getCookieId("c1", "test1.example.org", "/browser"), [
{name: "c1", value: JSON.stringify(["foo", "Bar", {foo: "Bar"}])},
{name: "c1.Path", value: "/browser"},
{name: "c1.HostOnly", value: "true"},
@ -42,9 +42,13 @@ const testCases = [
{name: "c1.2", value: "Object"},
{name: "c1.2.foo", value: "Bar"},
], true],
["c_encoded", [
{name: "c_encoded", value: encodeURIComponent(JSON.stringify({foo: {foo1: "bar"}}))}
]],
[
getCookieId("c_encoded", "test1.example.org",
"/browser/devtools/client/storage/test/"),
[
{name: "c_encoded", value: encodeURIComponent(JSON.stringify({foo: {foo1: "bar"}}))}
]
],
[null, [
{name: "c_encoded", value: "Object"},
{name: "c_encoded.foo", value: "Object"},

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

@ -24,6 +24,11 @@ const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/server/actors/storage.js,
// devtools/client/storage/ui.js and devtools/server/tests/browser/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
var gToolbox, gPanelWindow, gWindow, gUI;
// Services.prefs.setBoolPref(DUMPEMIT_PREF, true);
@ -505,10 +510,17 @@ function* selectTreeItem(ids) {
* The id of the row in the table widget
*/
function* selectTableItem(id) {
let selector = ".table-widget-cell[data-id='" + id + "']";
let table = gUI.table;
let selector = ".table-widget-column#" + table.uniqueId +
" .table-widget-cell[value='" + id + "']";
let target = gPanelWindow.document.querySelector(selector);
ok(target, "table item found with ids " + id);
if (!target) {
showAvailableIds();
}
yield click(target);
yield gUI.once("sidebar-updated");
}
@ -586,21 +598,38 @@ function getRowCells(id, includeHidden = false) {
if (!item) {
ok(false, "Row id '" + id + "' exists");
showAvailableIds();
}
let index = table.columns.get(table.uniqueId).visibleCellNodes.indexOf(item);
let index = table.columns.get(table.uniqueId).cellNodes.indexOf(item);
let cells = {};
for (let [name, column] of [...table.columns]) {
if (!includeHidden && column.column.parentNode.hidden) {
continue;
}
cells[name] = column.visibleCellNodes[index];
cells[name] = column.cellNodes[index];
}
return cells;
}
/**
* Show available ids.
*/
function showAvailableIds() {
let doc = gPanelWindow.document;
let table = gUI.table;
info("Available ids:");
let cells = doc.querySelectorAll(".table-widget-column#" + table.uniqueId +
" .table-widget-cell");
for (let cell of cells) {
info(" - " + cell.getAttribute("value"));
}
}
/**
* Get a cell value.
*
@ -798,9 +827,18 @@ function* checkState(state) {
is(items.size, names.length,
`There is correct number of rows in ${storeName}`);
if (names.length === 0) {
showAvailableIds();
}
for (let name of names) {
ok(items.has(name),
`There is item with name '${name}' in ${storeName}`);
if (!items.has(name)) {
showAvailableIds();
}
}
}
}
@ -838,3 +876,7 @@ var focusSearchBoxUsingShortcut = Task.async(function* (panelWin, callback) {
callback();
}
});
function getCookieId(name, domain, path) {
return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`;
}

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

@ -38,8 +38,10 @@ window.removeCookie = function(name, path) {
* can be tested.
*/
window.clear = function*() {
sessionStorage.clear();
localStorage.clear();
dump("removed localStorage from " + document.location + "\n");
sessionStorage.clear();
dump("removed sessionStorage from " + document.location + "\n");
};

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

@ -12,6 +12,12 @@ const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
const JSOL = require("devtools/client/shared/vendor/jsol");
const {KeyCodes} = require("devtools/client/shared/keycodes");
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/server/actors/storage.js,
// devtools/client/storage/test/head.js and
// devtools/server/tests/browser/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
loader.lazyRequireGetter(this, "TreeWidget",
"devtools/client/shared/widgets/TreeWidget", true);
loader.lazyRequireGetter(this, "TableWidget",
@ -36,13 +42,6 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = {
preventDescriptorModifiers: true
};
// Columns which are hidden by default in the storage table
const HIDDEN_COLUMNS = [
"creationTime",
"isDomain",
"isSecure"
];
const REASON = {
NEW_ROW: "new-row",
NEXT_50_ITEMS: "next-50-items",
@ -786,6 +785,8 @@ StorageUI.prototype = {
let uniqueKey = null;
let columns = {};
let editableFields = [];
let hiddenFields = [];
let privateFields = [];
let fields = yield this.getCurrentActor().getFields(subtype);
fields.forEach(f => {
@ -797,6 +798,14 @@ StorageUI.prototype = {
editableFields.push(f.name);
}
if (f.hidden) {
hiddenFields.push(f.name);
}
if (f.private) {
privateFields.push(f.name);
}
columns[f.name] = f.name;
let columnName;
try {
@ -812,7 +821,7 @@ StorageUI.prototype = {
}
});
this.table.setColumns(columns, null, HIDDEN_COLUMNS);
this.table.setColumns(columns, null, hiddenFields, privateFields);
this.hideSidebar();
yield this.makeFieldsEditable(editableFields);
@ -927,10 +936,13 @@ StorageUI.prototype = {
let rowId = this.table.contextMenuRowId;
let data = this.table.items.get(rowId);
let name = addEllipsis(data[this.table.uniqueId]);
let name = data[this.table.uniqueId];
let separatorRegex = new RegExp(SEPARATOR_GUID, "g");
let label = addEllipsis((name + "").replace(separatorRegex, "-"));
this._tablePopupDelete.setAttribute("label",
L10N.getFormatStr("storage.popupMenu.deleteLabel", name));
L10N.getFormatStr("storage.popupMenu.deleteLabel", label));
if (type === "cookies") {
let host = addEllipsis(data.host);

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

@ -15,6 +15,12 @@ const {isWindowIncluded} = require("devtools/shared/layout/utils");
const specs = require("devtools/shared/specs/storage");
const { Task } = require("devtools/shared/task");
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/client/storage/ui.js,
// devtools/client/storage/test/head.js and
// devtools/server/tests/browser/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
loader.lazyImporter(this, "Sqlite", "resource://gre/modules/Sqlite.jsm");
@ -467,14 +473,16 @@ StorageActors.createActor({
}
return {
uniqueKey: `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
`${SEPARATOR_GUID}${cookie.path}`,
name: cookie.name,
path: cookie.path || "",
host: cookie.host || "",
path: cookie.path || "",
// because expires is in seconds
expires: (cookie.expires || 0) * 1000,
// because it is in micro seconds
// because creationTime is in micro seconds
creationTime: cookie.creationTime / 1000,
// - do -
@ -495,7 +503,10 @@ StorageActors.createActor({
for (let cookie of cookies) {
if (this.isCookieAtHost(cookie, host)) {
this.hostVsStores.get(host).set(cookie.name, cookie);
let uniqueKey = `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
`${SEPARATOR_GUID}${cookie.path}`;
this.hostVsStores.get(host).set(uniqueKey, cookie);
}
}
},
@ -528,8 +539,11 @@ StorageActors.createActor({
case "changed":
if (hosts.length) {
for (let host of hosts) {
this.hostVsStores.get(host).set(subject.name, subject);
data[host] = [subject.name];
let uniqueKey = `${subject.name}${SEPARATOR_GUID}${subject.host}` +
`${SEPARATOR_GUID}${subject.path}`;
this.hostVsStores.get(host).set(uniqueKey, subject);
data[host] = [uniqueKey];
}
this.storageActor.update(action, "cookies", data);
}
@ -538,8 +552,11 @@ StorageActors.createActor({
case "deleted":
if (hosts.length) {
for (let host of hosts) {
this.hostVsStores.get(host).delete(subject.name);
data[host] = [subject.name];
let uniqueKey = `${subject.name}${SEPARATOR_GUID}${subject.host}` +
`${SEPARATOR_GUID}${subject.path}`;
this.hostVsStores.get(host).delete(uniqueKey);
data[host] = [uniqueKey];
}
this.storageActor.update("deleted", "cookies", data);
}
@ -550,8 +567,11 @@ StorageActors.createActor({
for (let host of hosts) {
let stores = [];
for (let cookie of subject) {
this.hostVsStores.get(host).delete(cookie.name);
stores.push(cookie.name);
let uniqueKey = `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
`${SEPARATOR_GUID}${cookie.path}`;
this.hostVsStores.get(host).delete(uniqueKey);
stores.push(uniqueKey);
}
data[host] = stores;
}
@ -573,15 +593,17 @@ StorageActors.createActor({
getFields: Task.async(function* () {
return [
{ name: "name", editable: 1},
{ name: "path", editable: 1},
{ name: "host", editable: 1},
{ name: "expires", editable: 1},
{ name: "lastAccessed", editable: 0},
{ name: "value", editable: 1},
{ name: "isDomain", editable: 0},
{ name: "isSecure", editable: 1},
{ name: "isHttpOnly", editable: 1}
{ name: "uniqueKey", editable: false, private: true },
{ name: "name", editable: true, hidden: false },
{ name: "host", editable: true, hidden: false },
{ name: "path", editable: true, hidden: false },
{ name: "expires", editable: true, hidden: false },
{ name: "lastAccessed", editable: false, hidden: false },
{ name: "creationTime", editable: false, hidden: true },
{ name: "value", editable: true, hidden: false },
{ name: "isDomain", editable: false, hidden: true },
{ name: "isSecure", editable: true, hidden: true },
{ name: "isHttpOnly", editable: true, hidden: false }
];
}),
@ -703,7 +725,7 @@ var cookieHelpers = {
* {
* host: "http://www.mozilla.org",
* field: "value",
* key: "name",
* editCookie: "name",
* oldValue: "%7BHello%7D",
* newValue: "%7BHelloo%7D",
* items: {
@ -727,10 +749,13 @@ var cookieHelpers = {
let origPath = field === "path" ? oldValue : data.items.path;
let cookie = null;
let enumerator = Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {});
let enumerator =
Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {});
while (enumerator.hasMoreElements()) {
let nsiCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (nsiCookie.name === origName && nsiCookie.host === origHost) {
if (nsiCookie.name === origName &&
nsiCookie.host === origHost &&
nsiCookie.path === origPath) {
cookie = {
host: nsiCookie.host,
path: nsiCookie.path,
@ -750,7 +775,7 @@ var cookieHelpers = {
return;
}
// If the date is expired set it for 1 minute in the future.
// If the date is expired set it for 10 seconds in the future.
let now = new Date();
if (!cookie.isSession && (cookie.expires * 1000) <= now) {
let tenSecondsFromNow = (now.getTime() + 10 * 1000) / 1000;
@ -804,6 +829,15 @@ var cookieHelpers = {
},
_removeCookies(host, opts = {}) {
// We use a uniqueId to emulate compound keys for cookies. We need to
// extract the cookie name to remove the correct cookie.
if (opts.name) {
let split = opts.name.split(SEPARATOR_GUID);
opts.name = split[0];
opts.path = split[2];
}
function hostMatches(cookieHost, matchHost) {
if (cookieHost == null) {
return matchHost == null;
@ -814,12 +848,15 @@ var cookieHelpers = {
return cookieHost == host;
}
let enumerator = Services.cookies.getCookiesFromHost(host, opts.originAttributes || {});
let enumerator =
Services.cookies.getCookiesFromHost(host, opts.originAttributes || {});
while (enumerator.hasMoreElements()) {
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (hostMatches(cookie.host, host) &&
(!opts.name || cookie.name === opts.name) &&
(!opts.domain || cookie.host === opts.domain)) {
(!opts.domain || cookie.host === opts.domain) &&
(!opts.path || cookie.path === opts.path)) {
Services.cookies.remove(
cookie.host,
cookie.name,
@ -1031,8 +1068,8 @@ function getObjectForLocalOrSessionStorage(type) {
getFields: Task.async(function* () {
return [
{ name: "name", editable: 1},
{ name: "value", editable: 1}
{ name: "name", editable: true },
{ name: "value", editable: true }
];
}),
@ -1212,8 +1249,8 @@ StorageActors.createActor({
getFields: Task.async(function* () {
return [
{ name: "url", editable: 0 },
{ name: "status", editable: 0 }
{ name: "url", editable: false },
{ name: "status", editable: false }
];
}),
@ -1736,26 +1773,26 @@ StorageActors.createActor({
// Detail of database
case "database":
return [
{ name: "objectStore", editable: 0 },
{ name: "keyPath", editable: 0 },
{ name: "autoIncrement", editable: 0 },
{ name: "indexes", editable: 0 },
{ name: "objectStore", editable: false },
{ name: "keyPath", editable: false },
{ name: "autoIncrement", editable: false },
{ name: "indexes", editable: false },
];
// Detail of object store
case "object store":
return [
{ name: "name", editable: 0 },
{ name: "value", editable: 0 }
{ name: "name", editable: false },
{ name: "value", editable: false }
];
// Detail of indexedDB for one origin
default:
return [
{ name: "db", editable: 0 },
{ name: "origin", editable: 0 },
{ name: "version", editable: 0 },
{ name: "objectStores", editable: 0 },
{ name: "db", editable: false },
{ name: "origin", editable: false },
{ name: "version", editable: false },
{ name: "objectStores", editable: false },
];
}
})
@ -2494,6 +2531,7 @@ let StorageActor = protocol.ActorClassWithSpec(specs.storageSpec, {
// added or changed update
this.removeNamesFromUpdateList("added", storeType, data);
this.removeNamesFromUpdateList("changed", storeType, data);
for (let host in data) {
if (data[host].length == 0 && this.boundUpdate.added &&
this.boundUpdate.added[storeType] &&

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

@ -11,6 +11,7 @@ support-files =
doc_perf.html
navigate-first.html
navigate-second.html
storage-cookies-same-name.html
storage-dynamic-windows.html
storage-listings.html
storage-unsecured-iframe.html
@ -80,6 +81,7 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di
#[browser_perf-front-profiler-01.js] bug 1077464
#[browser_perf-front-profiler-05.js] bug 1077464
#[browser_perf-front-profiler-06.js]
[browser_storage_cookies-duplicate-names.js]
[browser_storage_dynamic_windows.js]
[browser_storage_listings.js]
[browser_storage_updates.js]

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

@ -0,0 +1,105 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the storage panel is able to display multiple cookies with the same
// name (and different paths).
const {StorageFront} = require("devtools/shared/fronts/storage");
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
const TESTDATA = {
"test1.example.org": [
{
name: "name",
value: "value1",
expires: 0,
path: "/",
host: "test1.example.org",
isDomain: false,
isSecure: false,
},
{
name: "name",
value: "value2",
expires: 0,
path: "/path2/",
host: "test1.example.org",
isDomain: false,
isSecure: false,
},
{
name: "name",
value: "value3",
expires: 0,
path: "/path3/",
host: "test1.example.org",
isDomain: false,
isSecure: false,
}
]
};
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies-same-name.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = StorageFront(client, form);
let data = yield front.listStores();
ok(data.cookies, "Cookies storage actor is present");
yield testCookies(data.cookies);
yield clearStorage();
// Forcing GC/CC to get rid of docshells and windows created by this test.
forceCollections();
yield client.close();
forceCollections();
DebuggerServer.destroy();
forceCollections();
});
function testCookies(cookiesActor) {
let numHosts = Object.keys(cookiesActor.hosts).length;
is(numHosts, 1, "Correct number of host entries for cookies");
return testCookiesObjects(0, cookiesActor.hosts, cookiesActor);
}
var testCookiesObjects = Task.async(function* (index, hosts, cookiesActor) {
let host = Object.keys(hosts)[index];
let matchItems = data => {
is(data.total, TESTDATA[host].length,
"Number of cookies in host " + host + " matches");
for (let item of data.data) {
let found = false;
for (let toMatch of TESTDATA[host]) {
if (item.name === toMatch.name &&
item.host === toMatch.host &&
item.path === toMatch.path) {
found = true;
ok(true, "Found cookie " + item.name + " in response");
is(item.value.str, toMatch.value, "The value matches.");
is(item.expires, toMatch.expires, "The expiry time matches.");
is(item.path, toMatch.path, "The path matches.");
is(item.host, toMatch.host, "The host matches.");
is(item.isSecure, toMatch.isSecure, "The isSecure value matches.");
is(item.isDomain, toMatch.isDomain, "The isDomain value matches.");
break;
}
}
ok(found, "cookie " + item.name + " should exist in response");
}
};
ok(!!TESTDATA[host], "Host is present in the list : " + host);
matchItems(yield cookiesActor.getStoreObjects(host));
if (index == Object.keys(hosts).length - 1) {
return;
}
yield testCookiesObjects(++index, hosts, cookiesActor);
});

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

@ -66,6 +66,7 @@ function markOutMatched(toBeEmptied, data, deleted) {
info("Testing for " + storageType);
for (let host in data[storageType]) {
ok(toBeEmptied[storageType][host], "Host " + host + " found");
if (!deleted) {
for (let item of data[storageType][host]) {
let index = toBeEmptied[storageType][host].indexOf(item);
@ -87,50 +88,6 @@ function markOutMatched(toBeEmptied, data, deleted) {
}
}
// function testReload(front) {
// info("Testing if reload works properly");
// let shouldBeEmptyFirst = Cu.cloneInto(beforeReload, {});
// let shouldBeEmptyLast = Cu.cloneInto(beforeReload, {});
// return new Promise(resolve => {
// let onStoresUpdate = data => {
// info("in stores update of testReload");
// // This might be second time stores update is happening, in which case,
// // data.deleted will be null.
// // OR.. This might be the first time on a super slow machine where both
// // data.deleted and data.added is missing in the first update.
// if (data.deleted) {
// markOutMatched(shouldBeEmptyFirst, data.deleted, true);
// }
// if (!Object.keys(shouldBeEmptyFirst).length) {
// info("shouldBeEmptyFirst is empty now");
// }
// // stores-update call might not have data.added for the first time on
// // slow machines, in which case, data.added will be null
// if (data.added) {
// markOutMatched(shouldBeEmptyLast, data.added);
// }
// if (!Object.keys(shouldBeEmptyLast).length) {
// info("Everything to be received is received.");
// endTestReloaded();
// }
// };
// let endTestReloaded = () => {
// front.off("stores-update", onStoresUpdate);
// resolve();
// };
// front.on("stores-update", onStoresUpdate);
// content.location.reload();
// });
// }
function testAddIframe(front) {
info("Testing if new iframe addition works properly");
return new Promise(resolve => {
@ -142,7 +99,10 @@ function testAddIframe(front) {
"https://sectest1.example.org": ["iframe-s-ss1"]
},
cookies: {
"sectest1.example.org": ["sc1"]
"sectest1.example.org": [
getCookieId("sc1", "sectest1.example.org",
"/browser/devtools/server/tests/browser/")
]
},
indexedDB: {
// empty because indexed db creation happens after the page load, so at
@ -150,7 +110,7 @@ function testAddIframe(front) {
"https://sectest1.example.org": []
},
Cache: {
"https://sectest1.example.org":[]
"https://sectest1.example.org": []
}
};

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

@ -35,15 +35,6 @@ const storeMap = {
path: "/",
host: "test1.example.org",
isDomain: false,
isSecure: false,
},
{
name: "uc1",
value: "foobar",
host: ".example.org",
path: "/",
expires: 0,
isDomain: true,
isSecure: true,
}
],
@ -337,7 +328,8 @@ function* testStores(data) {
}
function testCookies(cookiesActor) {
is(Object.keys(cookiesActor.hosts).length, 2, "Correct number of host entries for cookies");
is(Object.keys(cookiesActor.hosts).length, 2,
"Correct number of host entries for cookies");
return testCookiesObjects(0, cookiesActor.hosts, cookiesActor);
}
@ -346,9 +338,9 @@ var testCookiesObjects = Task.async(function* (index, hosts, cookiesActor) {
let matchItems = data => {
let cookiesLength = 0;
for (let secureCookie of storeMap.cookies[host]) {
if (secureCookie.isSecure) {
++cookiesLength;
}
if (secureCookie.isSecure) {
++cookiesLength;
}
}
// Any secure cookies did not get stored in the database.
is(data.total, storeMap.cookies[host].length - cookiesLength,

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

@ -27,7 +27,12 @@ const TESTS = [
expected: {
added: {
cookies: {
"test1.example.org": ["c1", "c2"]
"test1.example.org": [
getCookieId("c1", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
getCookieId("c2", "test1.example.org",
"/browser/devtools/server/tests/browser/")
]
},
localStorage: {
"http://test1.example.org": ["l1"]
@ -48,7 +53,10 @@ const TESTS = [
expected: {
changed: {
cookies: {
"test1.example.org": ["c1"]
"test1.example.org": [
getCookieId("c1", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
}
},
added: {
@ -74,7 +82,10 @@ const TESTS = [
expected: {
deleted: {
cookies: {
"test1.example.org": ["c2"]
"test1.example.org": [
getCookieId("c2", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
},
localStorage: {
"http://test1.example.org": ["l1"]
@ -112,7 +123,10 @@ const TESTS = [
expected: {
added: {
cookies: {
"test1.example.org": ["c3"]
"test1.example.org": [
getCookieId("c3", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
},
sessionStorage: {
"http://test1.example.org": ["s1", "s2"]
@ -125,7 +139,10 @@ const TESTS = [
},
deleted: {
cookies: {
"test1.example.org": ["c1"]
"test1.example.org": [
getCookieId("c1", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
},
localStorage: {
"http://test1.example.org": ["l2"]
@ -158,7 +175,10 @@ const TESTS = [
expected: {
deleted: {
cookies: {
"test1.example.org": ["c3"]
"test1.example.org": [
getCookieId("c3", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
}
}
}

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

@ -2,6 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* eslint no-unused-vars: [2, {"vars": "local"}] */
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
@ -19,6 +23,11 @@ const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/server/actors/storage.js,
// devtools/client/storage/ui.js and devtools/client/storage/test/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
// All tests are asynchronous.
waitForExplicitFinish();
@ -94,7 +103,6 @@ function once(target, eventName, useCapture = false) {
info("Waiting for event: '" + eventName + "' on " + target + ".");
return new Promise(resolve => {
for (let [add, remove] of [
["addEventListener", "removeEventListener"],
["addListener", "removeListener"],
@ -137,6 +145,8 @@ function getMockTabActor(win) {
}
registerCleanupFunction(function tearDown() {
Services.cookies.removeAll();
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
@ -148,8 +158,11 @@ function idleWait(time) {
function busyWait(time) {
let start = Date.now();
// eslint-disable-next-line
let stack;
while (Date.now() - start < time) { stack = Components.stack; }
while (Date.now() - start < time) {
stack = Components.stack;
}
}
/**
@ -172,11 +185,12 @@ function waitUntil(predicate, interval = 10) {
}
function waitForMarkerType(front, types, predicate,
unpackFun = (name, data) => data.markers,
eventName = "timeline-data")
{
unpackFun = (name, data) => data.markers,
eventName = "timeline-data") {
types = [].concat(types);
predicate = predicate || function () { return true; };
predicate = predicate || function () {
return true;
};
let filteredMarkers = [];
let { promise, resolve } = defer();
@ -190,9 +204,11 @@ function waitForMarkerType(front, types, predicate,
let markers = unpackFun(name, data);
info("Got markers: " + JSON.stringify(markers, null, 2));
filteredMarkers = filteredMarkers.concat(markers.filter(m => types.indexOf(m.name) !== -1));
filteredMarkers = filteredMarkers.concat(
markers.filter(m => types.indexOf(m.name) !== -1));
if (types.every(t => filteredMarkers.some(m => m.name === t)) && predicate(filteredMarkers)) {
if (types.every(t => filteredMarkers.some(m => m.name === t)) &&
predicate(filteredMarkers)) {
front.off(eventName, handler);
resolve(filteredMarkers);
}
@ -201,3 +217,7 @@ function waitForMarkerType(front, types, predicate,
return promise;
}
function getCookieId(name, domain, path) {
return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`;
}

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

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Storage inspector cookies with duplicate names</title>
</head>
<body onload="createCookies()">
<script type="application/javascript;version=1.7">
"use strict";
function createCookies() {
document.cookie = "name=value1;path=/;";
document.cookie = "name=value2;path=/path2/;";
document.cookie = "name=value3;path=/path3/;";
}
window.removeCookie = function (name) {
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
};
window.clearCookies = function () {
let cookies = document.cookie;
for (let cookie of cookies.split(";")) {
removeCookie(cookie.split("=")[0]);
}
};
</script>
</body>
</html>

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

@ -1525,6 +1525,7 @@ function NetworkMonitorChild(outerWindowID, messageManager, conn, owner) {
this._onNewEvent = this._onNewEvent.bind(this);
this._onUpdateEvent = this._onUpdateEvent.bind(this);
this._netEvents = new Map();
this._msgName = `debug:${this.conn.prefix}netmonitor`;
}
exports.NetworkMonitorChild = NetworkMonitorChild;
@ -1542,7 +1543,7 @@ NetworkMonitorChild.prototype = {
set saveRequestAndResponseBodies(val) {
this._saveRequestAndResponseBodies = val;
this._messageManager.sendAsyncMessage("debug:netmonitor", {
this._messageManager.sendAsyncMessage(this._msgName, {
action: "setPreferences",
preferences: {
saveRequestAndResponseBodies: this._saveRequestAndResponseBodies,
@ -1557,7 +1558,7 @@ NetworkMonitorChild.prototype = {
set throttleData(val) {
this._throttleData = val;
this._messageManager.sendAsyncMessage("debug:netmonitor", {
this._messageManager.sendAsyncMessage(this._msgName, {
action: "setPreferences",
preferences: {
throttleData: this._throttleData,
@ -1572,11 +1573,9 @@ NetworkMonitorChild.prototype = {
});
let mm = this._messageManager;
mm.addMessageListener("debug:netmonitor:newEvent",
this._onNewEvent);
mm.addMessageListener("debug:netmonitor:updateEvent",
this._onUpdateEvent);
mm.sendAsyncMessage("debug:netmonitor", {
mm.addMessageListener(`${this._msgName}:newEvent`, this._onNewEvent);
mm.addMessageListener(`${this._msgName}:updateEvent`, this._onUpdateEvent);
mm.sendAsyncMessage(this._msgName, {
outerWindowID: this.outerWindowID,
action: "start",
});
@ -1600,13 +1599,12 @@ NetworkMonitorChild.prototype = {
let weakActor = this._netEvents.get(id);
let actor = weakActor ? weakActor.get() : null;
if (!actor) {
console.error("Received debug:netmonitor:updateEvent for unknown " +
"event ID: " + id);
console.error(`Received ${this._msgName}:updateEvent for unknown event ID: ${id}`);
return;
}
if (!(method in actor)) {
console.error("Received debug:netmonitor:updateEvent unsupported " +
"method: " + method);
console.error(`Received ${this._msgName}:updateEvent unsupported ` +
`method: ${method}`);
return;
}
actor[method].apply(actor, args);
@ -1615,10 +1613,8 @@ NetworkMonitorChild.prototype = {
destroy: function () {
let mm = this._messageManager;
try {
mm.removeMessageListener("debug:netmonitor:newEvent",
this._onNewEvent);
mm.removeMessageListener("debug:netmonitor:updateEvent",
this._onUpdateEvent);
mm.removeMessageListener(`${this._msgName}:newEvent`, this._onNewEvent);
mm.removeMessageListener(`${this._msgName}:updateEvent`, this._onUpdateEvent);
} catch (e) {
// On b2g, when registered to a new root docshell,
// all message manager functions throw when trying to call them during
@ -1647,10 +1643,13 @@ NetworkMonitorChild.prototype = {
* @param nsIMessageManager messageManager
* The message manager for the child app process. This is used for
* communication with the NetworkMonitorChild instance of the process.
* @param string msgName
* The message name to be used for this connection.
*/
function NetworkEventActorProxy(messageManager) {
function NetworkEventActorProxy(messageManager, msgName) {
this.id = gSequenceId();
this.messageManager = messageManager;
this._msgName = msgName;
}
exports.NetworkEventActorProxy = NetworkEventActorProxy;
@ -1658,7 +1657,7 @@ NetworkEventActorProxy.methodFactory = function (method) {
return DevToolsUtils.makeInfallible(function () {
let args = Array.slice(arguments);
let mm = this.messageManager;
mm.sendAsyncMessage("debug:netmonitor:updateEvent", {
mm.sendAsyncMessage(`${this._msgName}:updateEvent`, {
id: this.id,
method: method,
args: args,
@ -1678,7 +1677,7 @@ NetworkEventActorProxy.prototype = {
*/
init: DevToolsUtils.makeInfallible(function (event) {
let mm = this.messageManager;
mm.sendAsyncMessage("debug:netmonitor:newEvent", {
mm.sendAsyncMessage(`${this._msgName}:newEvent`, {
id: this.id,
event: event,
});
@ -1726,6 +1725,7 @@ exports.setupParentProcess = setupParentProcess;
* The RDP connection prefix that uniquely identifies the connection.
*/
function NetworkMonitorParent(mm, prefix) {
this._msgName = `debug:${prefix}netmonitor`;
this.onNetMonitorMessage = this.onNetMonitorMessage.bind(this);
this.onNetworkEvent = this.onNetworkEvent.bind(this);
this.setMessageManager(mm);
@ -1738,16 +1738,16 @@ NetworkMonitorParent.prototype = {
setMessageManager(mm) {
if (this.messageManager) {
let oldMM = this.messageManager;
oldMM.removeMessageListener("debug:netmonitor", this.onNetMonitorMessage);
oldMM.removeMessageListener(this._msgName, this.onNetMonitorMessage);
}
this.messageManager = mm;
if (mm) {
mm.addMessageListener("debug:netmonitor", this.onNetMonitorMessage);
mm.addMessageListener(this._msgName, this.onNetMonitorMessage);
}
},
/**
* Handler for "debug:netmonitor" messages received through the message manager
* Handler for `debug:${prefix}netmonitor` messages received through the message manager
* from the content process.
*
* @param object msg
@ -1802,7 +1802,7 @@ NetworkMonitorParent.prototype = {
* data about the request is available.
*/
onNetworkEvent: DevToolsUtils.makeInfallible(function (event) {
return new NetworkEventActorProxy(this.messageManager).init(event);
return new NetworkEventActorProxy(this.messageManager, this._msgName).init(event);
}),
destroy: function () {

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

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test for Bug 1223561</title>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
<script type="text/javascript" src="file_fullscreen-utils.js"></script>
</head>
<body>
<table style="background-color: green"></table>
<script>
"use strict";
function ok(condition, msg) {
opener.ok(condition, "[table] " + msg);
}
function is(a, b, msg) {
opener.is(a, b, "[table] " + msg);
}
function info(msg) {
opener.info("[table] " + msg);
}
const gTable = document.querySelector("table");
function begin() {
info("The default background of window should be white");
addFullscreenChangeContinuation("enter", enteredFullscreen);
assertWindowPureColor(window, "white");
gTable.requestFullscreen();
}
function enteredFullscreen() {
info("The table with green background should be in fullscreen");
assertWindowPureColor(window, "green");
gTable.style = "background: transparent";
info("When the table becames transparent, the black backdrop should appear");
assertWindowPureColor(window, "black");
addFullscreenChangeContinuation("exit", exitedFullscreen);
document.exitFullscreen();
}
function exitedFullscreen() {
opener.nextTest();
}
</script>
</body>
</html>

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

@ -53,28 +53,6 @@ support-files =
file_bug1260704.png
file_formSubmission_img.jpg
file_formSubmission_text.txt
file_fullscreen-api.html
file_fullscreen-backdrop.html
file_fullscreen-denied-inner.html
file_fullscreen-denied.html
file_fullscreen-esc-exit-inner.html
file_fullscreen-esc-exit.html
file_fullscreen-hidden.html
file_fullscreen-lenient-setters.html
file_fullscreen-multiple-inner.html
file_fullscreen-multiple.html
file_fullscreen-navigation.html
file_fullscreen-nested.html
file_fullscreen-prefixed.html
file_fullscreen-plugins.html
file_fullscreen-rollback.html
file_fullscreen-scrollbar.html
file_fullscreen-selector.html
file_fullscreen-svg-element.html
file_fullscreen-top-layer.html
file_fullscreen-unprefix-disabled-inner.html
file_fullscreen-unprefix-disabled.html
file_fullscreen-utils.js
file_iframe_sandbox_a_if1.html
file_iframe_sandbox_a_if10.html
file_iframe_sandbox_a_if11.html
@ -461,6 +439,30 @@ skip-if = toolkit == 'android'
[test_fullscreen-api.html]
tags = fullscreen
skip-if = toolkit == 'android'
support-files =
file_fullscreen-api.html
file_fullscreen-backdrop.html
file_fullscreen-denied-inner.html
file_fullscreen-denied.html
file_fullscreen-esc-exit-inner.html
file_fullscreen-esc-exit.html
file_fullscreen-hidden.html
file_fullscreen-lenient-setters.html
file_fullscreen-multiple-inner.html
file_fullscreen-multiple.html
file_fullscreen-navigation.html
file_fullscreen-nested.html
file_fullscreen-prefixed.html
file_fullscreen-plugins.html
file_fullscreen-rollback.html
file_fullscreen-scrollbar.html
file_fullscreen-selector.html
file_fullscreen-svg-element.html
file_fullscreen-table.html
file_fullscreen-top-layer.html
file_fullscreen-unprefix-disabled-inner.html
file_fullscreen-unprefix-disabled.html
file_fullscreen-utils.js
[test_fullscreen-api-race.html]
tags = fullscreen
skip-if = toolkit == 'android' # just copy the conditions from the test above

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

@ -45,6 +45,7 @@ var gTestWindows = [
"file_fullscreen-prefixed.html",
"file_fullscreen-unprefix-disabled.html",
"file_fullscreen-lenient-setters.html",
"file_fullscreen-table.html",
];
var testWindow = null;

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

@ -21,9 +21,6 @@ SimpleTest.waitForExplicitFinish();
// 6. Add 1.6s of data once video element fired waiting, that canplay is fired once readyState is HAVE_FUTURE_DATA.
// 7. Finally load data to the end and ensure that canplaythrough is fired and that readyState is now HAVE_ENOUGH_DATA
// FIXME: bug 1319293
addMSEPrefs(["media.dormant-on-pause-timeout-ms", -1]);
runWithMSE(function(ms, el) {
el.controls = true;
once(ms, 'sourceopen').then(function() {

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

@ -154,7 +154,7 @@ VorbisDataDecoder::DoDecode(MediaRawData* aSample)
const unsigned char* aData = aSample->Data();
size_t aLength = aSample->Size();
int64_t aOffset = aSample->mOffset;
uint64_t aTstampUsecs = aSample->mTime;
int64_t aTstampUsecs = aSample->mTime;
int64_t aTotalFrames = 0;
MOZ_ASSERT(mPacketCount >= 3);

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

@ -373,12 +373,21 @@ nsresult
MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
{
mDecoder = CreateDecoder(mMimeType);
if (!mDecoder) {
INVOKE_CALLBACK(Error,
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__));
return NS_ERROR_FAILURE;
}
// Check if the video codec supports adaptive playback or not.
if (aSurface && java::HardwareCodecCapabilityUtils::CheckSupportsAdaptivePlayback(
mDecoder, nsCString(TranslateMimeType(mMimeType)))) {
// TODO: may need to find a way to not use hard code to decide the max w/h.
mFormat->SetInteger(MediaFormat::KEY_MAX_WIDTH, 1920);
mFormat->SetInteger(MediaFormat::KEY_MAX_HEIGHT, 1080);
}
MediaCrypto::LocalRef crypto = MediaDrmProxy::GetMediaCrypto(mDrmStubId);
bool hascrypto = !!crypto;
LOG("Has(%d) MediaCrypto (%s)", hascrypto, NS_ConvertUTF16toUTF8(mDrmStubId).get());

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

@ -255,7 +255,7 @@ int32_t sAggregateBatchTestRedoOrderArr[] = {
class TestTransaction : public nsITransaction
{
protected:
virtual ~TestTransaction() {}
virtual ~TestTransaction() = default;
public:
@ -288,11 +288,9 @@ public:
: mVal(++sConstructorCount), mFlags(aFlags)
{}
virtual ~SimpleTransaction()
{
}
~SimpleTransaction() override = default;
NS_IMETHOD DoTransaction()
NS_IMETHOD DoTransaction() override
{
//
// Make sure DoTransaction() is called in the order we expect!
@ -309,7 +307,7 @@ public:
return (mFlags & THROWS_DO_ERROR_FLAG) ? NS_ERROR_FAILURE : NS_OK;
}
NS_IMETHOD UndoTransaction()
NS_IMETHOD UndoTransaction() override
{
//
// Make sure UndoTransaction() is called in the order we expect!
@ -326,7 +324,7 @@ public:
return (mFlags & THROWS_UNDO_ERROR_FLAG) ? NS_ERROR_FAILURE : NS_OK;
}
NS_IMETHOD RedoTransaction()
NS_IMETHOD RedoTransaction() override
{
//
// Make sure RedoTransaction() is called in the order we expect!
@ -343,7 +341,7 @@ public:
return (mFlags & THROWS_REDO_ERROR_FLAG) ? NS_ERROR_FAILURE : NS_OK;
}
NS_IMETHOD GetIsTransient(bool *aIsTransient)
NS_IMETHOD GetIsTransient(bool *aIsTransient) override
{
if (aIsTransient) {
*aIsTransient = (mFlags & TRANSIENT_FLAG) ? true : false;
@ -351,7 +349,7 @@ public:
return NS_OK;
}
NS_IMETHOD Merge(nsITransaction *aTransaction, bool *aDidMerge)
NS_IMETHOD Merge(nsITransaction *aTransaction, bool *aDidMerge) override
{
if (aDidMerge) {
*aDidMerge = (mFlags & MERGE_FLAG) ? true : false;
@ -403,11 +401,9 @@ public:
mNumChildrenPerNode = aNumChildrenPerNode;
}
virtual ~AggregateTransaction()
{
}
~AggregateTransaction() override = default;
NS_IMETHOD DoTransaction()
NS_IMETHOD DoTransaction() override
{
if (mLevel >= mMaxLevel) {
// Only leaf nodes can throw errors!
@ -479,7 +475,7 @@ class SimpleTransactionFactory : public TestTransactionFactory
{
public:
TestTransaction *create(nsITransactionManager *txmgr, int32_t flags)
TestTransaction *create(nsITransactionManager *txmgr, int32_t flags) override
{
return (TestTransaction *)new SimpleTransaction(flags);
}
@ -502,7 +498,7 @@ public:
{
}
virtual TestTransaction *create(nsITransactionManager *txmgr, int32_t flags)
TestTransaction *create(nsITransactionManager *txmgr, int32_t flags) override
{
return (TestTransaction *)new AggregateTransaction(txmgr, mMaxLevel,
mNumChildrenPerNode,

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

@ -358,7 +358,7 @@ public:
RefPtr<Layer> mRoot;
RefPtr<GeckoContentController> mController;
APZCTreeManagerParent* mApzcTreeManagerParent;
CompositorBridgeParent* mParent;
RefPtr<CompositorBridgeParent> mParent;
LayerManagerComposite* mLayerManager;
// Pointer to the CrossProcessCompositorBridgeParent. Used by APZCs to share
// their FrameMetrics with the corresponding child process that holds

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

@ -264,7 +264,7 @@ public:
protected:
virtual void GetCurrentInformationInternal(InfoType*) = 0;
virtual void OnNotificationsDisabled() {
void OnNotificationsDisabled() override {
mHasValidCache = false;
}
@ -276,15 +276,15 @@ private:
class BatteryObserversManager : public CachingObserversManager<BatteryInformation>
{
protected:
void EnableNotifications() {
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableBatteryNotifications());
}
void DisableNotifications() {
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableBatteryNotifications());
}
void GetCurrentInformationInternal(BatteryInformation* aInfo) {
void GetCurrentInformationInternal(BatteryInformation* aInfo) override {
PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo));
}
};
@ -300,15 +300,15 @@ BatteryObservers()
class NetworkObserversManager : public CachingObserversManager<NetworkInformation>
{
protected:
void EnableNotifications() {
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableNetworkNotifications());
}
void DisableNotifications() {
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableNetworkNotifications());
}
void GetCurrentInformationInternal(NetworkInformation* aInfo) {
void GetCurrentInformationInternal(NetworkInformation* aInfo) override {
PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo));
}
};
@ -324,11 +324,11 @@ NetworkObservers()
class WakeLockObserversManager : public ObserversManager<WakeLockInformation>
{
protected:
void EnableNotifications() {
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
}
void DisableNotifications() {
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
}
};
@ -344,15 +344,15 @@ WakeLockObservers()
class ScreenConfigurationObserversManager : public CachingObserversManager<ScreenConfiguration>
{
protected:
void EnableNotifications() {
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableScreenConfigurationNotifications());
}
void DisableNotifications() {
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableScreenConfigurationNotifications());
}
void GetCurrentInformationInternal(ScreenConfiguration* aInfo) {
void GetCurrentInformationInternal(ScreenConfiguration* aInfo) override {
PROXY_IF_SANDBOXED(GetCurrentScreenConfiguration(aInfo));
}
};
@ -449,11 +449,11 @@ void SetScreenBrightness(double aBrightness)
class SystemClockChangeObserversManager : public ObserversManager<int64_t>
{
protected:
void EnableNotifications() {
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableSystemClockChangeNotifications());
}
void DisableNotifications() {
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableSystemClockChangeNotifications());
}
};
@ -489,11 +489,11 @@ NotifySystemClockChange(const int64_t& aClockDeltaMS)
class SystemTimezoneChangeObserversManager : public ObserversManager<SystemTimezoneChangeInformation>
{
protected:
void EnableNotifications() {
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableSystemTimezoneChangeNotifications());
}
void DisableNotifications() {
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableSystemTimezoneChangeNotifications());
}
};

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

@ -15,7 +15,7 @@ include $(topsrcdir)/config/rules.mk
# NB: the IPDL compiler manages .ipdl-->.h/.cpp dependencies itself,
# which is why we don't have explicit .h/.cpp targets here
export:: $(ALL_IPDLSRCS)
ipdl: $(ALL_IPDLSRCS)
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) \
$(srcdir)/ipdl.py \
@ -23,5 +23,9 @@ export:: $(ALL_IPDLSRCS)
--outcpp-dir=. \
$(IPDLDIRS:%=-I%) \
$^
.PHONY: ipdl
export:: ipdl
endif

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

@ -423,7 +423,7 @@ class StructType(_CompoundType):
def itercomponents(self):
for f in self.fields:
yield f
def name(self): return self.qname.baseid
def fullname(self): return str(self.qname)
@ -552,7 +552,7 @@ class SymbolTable:
self.scopes = [ { } ] # stack({})
self.globalScope = self.scopes[0]
self.currentScope = self.globalScope
def enterScope(self, node):
assert (isinstance(self.scopes[0], dict)
and self.globalScope is self.scopes[0])
@ -843,7 +843,7 @@ class GatherDecls(TcheckVisitor):
# If we've already processed this union, don't do it again.
if len(utype.components):
return
for c in ud.components:
cdecl = self.symtab.lookup(str(c))
if cdecl is None:
@ -930,7 +930,7 @@ class GatherDecls(TcheckVisitor):
mgdname, p.name)
p.states = { }
if len(p.transitionStmts):
p.startStates = [ ts for ts in p.transitionStmts
if ts.state.start ]
@ -966,7 +966,7 @@ class GatherDecls(TcheckVisitor):
and None is self.symtab.lookup(_DELETE_MSG))):
# add a special state |state DEAD: null goto DEAD;|
deadtrans = TransitionStmt.makeNullStmt(State.DEAD)
p.states[State.DEAD] = deadtrans
p.states[State.DEAD] = deadtrans
if p.decl.type.hasReentrantDelete:
dyingtrans = TransitionStmt.makeNullStmt(State.DYING)
p.states[State.DYING] = dyingtrans
@ -979,7 +979,7 @@ class GatherDecls(TcheckVisitor):
# already resolved this guy's state
if isinstance(actortype.state, Decl):
return
if actortype.state is None:
# we thought this was a C++ type until type checking,
# when we realized it was an IPDL actor type. But
@ -1182,7 +1182,7 @@ class GatherDecls(TcheckVisitor):
mdecl = self.symtab.lookup(mname)
if mdecl is not None and mdecl.type.isIPDL() and mdecl.type.isProtocol():
mdecl = self.symtab.lookup(mname +'Constructor')
if mdecl is None:
self.error(loc, "message `%s' has not been declared", mname)
elif not mdecl.type.isMessage():
@ -1247,7 +1247,7 @@ def checkcycles(p, stack=None):
# special case for self-managed protocols
if cp is p:
continue
if cp in stack:
return [stack + [p, cp]]
cycles += checkcycles(cp, stack + [p])
@ -1328,7 +1328,7 @@ class CheckTypes(TcheckVisitor):
def visitProtocol(self, p):
self.ptype = p.decl.type
# check that we require no more "power" than our manager protocols
ptype, pname = p.decl.type, p.decl.shortname
@ -1369,7 +1369,7 @@ class CheckTypes(TcheckVisitor):
if cycles:
self.error(
p.decl.loc,
"cycle(s) detected in manager/manages heirarchy: %s",
"cycle(s) detected in manager/manages hierarchy: %s",
formatcycles(cycles))
if 1 == len(ptype.managers) and ptype is ptype.manager():
@ -1454,7 +1454,7 @@ class CheckTypes(TcheckVisitor):
def visitManager(self, mgr):
# FIXME/bug 541126: check that the protocol graph is acyclic
pdecl = mgr.of.decl
ptype, pname = pdecl.type, pdecl.shortname
@ -1523,7 +1523,7 @@ class CheckTypes(TcheckVisitor):
if mtype.isCtor() and not ptype.isManagerOf(mtype.constructedType()):
self.error(
loc,
"ctor for protocol `%s', which is not managed by protocol `%s'",
"ctor for protocol `%s', which is not managed by protocol `%s'",
mname[:-len('constructor')], pname)
@ -1535,7 +1535,7 @@ class CheckTypes(TcheckVisitor):
SEND: [ OUT, _YNC ], RECV: [ IN, _YNC ],
CALL: [ OUT, INTR ], ANSWER: [ IN, INTR ],
} [t.trigger]
if (OUT is impliedDirection and t.msg.type.isIn()
or IN is impliedDirection and t.msg.type.isOut()
or _YNC is impliedSems and t.msg.type.isInterrupt()
@ -2147,7 +2147,7 @@ direction as trigger |t|'''
U2 = stateName(U2)
return T1, M1.msg.progname, U1, T2, M2.msg.progname, U2
def reportRaceError(self, loc, S, t1Seq, t2Seq):
T1, M1, U1, T2, M2, U2 = self._normalizeTransitionSequences(t1Seq, t2Seq)

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

@ -23,7 +23,7 @@ class IPDLCompile:
'-d', tmpoutdir,
self.specfilename
])
proc = subprocess.Popen(args=self.argv,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
@ -69,7 +69,7 @@ exception being raised.'''
def ok(self):
'''Return True iff compiling self.specstring was successful.'''
assert self.completed()
return (not self.exception()
and not self.error()
and (0 == self.returncode))

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

@ -22,7 +22,7 @@ class IPDLTestCase(unittest.TestCase):
def shortDescription(self):
return '%s test of "%s"'% (self.__class__.__name__, self.filename)
class OkTestCase(IPDLTestCase):
'''An invocation of the IPDL compiler on a valid specification.
@ -34,7 +34,7 @@ The IPDL compiler should not produce errors or exceptions.'''
def checkPassed(self):
self.assertTrue(self.compile.ok(), self.mkFailMsg())
class ErrorTestCase(IPDLTestCase):
'''An invocation of the IPDL compiler on an *invalid* specification.
The IPDL compiler *should* produce errors but not exceptions.'''

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

@ -206,10 +206,26 @@ nsPlaceholderFrame::GetParentStyleContext(nsIFrame** aProviderFrame) const
}
}
nsIFrame* parentFrame = GetParent();
// Placeholder of backdrop frame is a child of the corresponding top
// layer frame, and its style context inherits from that frame. In
// case of table, the top layer frame is the table wrapper frame.
// However, it will be skipped in CorrectStyleParentFrame below, so
// we need to handle it specially here.
if ((GetStateBits() & PLACEHOLDER_FOR_TOPLAYER) &&
parentFrame->GetType() == nsGkAtoms::tableWrapperFrame) {
MOZ_ASSERT(mOutOfFlowFrame->GetType() == nsGkAtoms::backdropFrame,
"Only placeholder of backdrop frame can be put inside "
"a table wrapper frame");
*aProviderFrame = parentFrame;
return parentFrame->StyleContext();
}
// Lie about our pseudo so we can step out of all anon boxes and
// pseudo-elements. The other option would be to reimplement the
// {ib} split gunk here.
*aProviderFrame = CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame);
*aProviderFrame = CorrectStyleParentFrame(parentFrame,
nsGkAtoms::placeholderFrame);
return *aProviderFrame ? (*aProviderFrame)->StyleContext() : nullptr;
}

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

@ -22,7 +22,11 @@
}
*|*::-moz-table-wrapper {
/* The inherited properties here need to be safe to have on both the
* table and the table wrapper, generally because code ignores them
* for the table. */
display: inherit !important; /* table or inline-table */
-moz-top-layer: inherit !important;
margin: inherit ! important;
padding: 0 ! important;
border: none ! important;

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

@ -7,6 +7,8 @@ package org.mozilla.gecko.icons.loader;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
@ -82,13 +84,10 @@ public class IconDownloader implements IconLoader {
* @param targetFaviconURL URL of the favicon to download.
* @return A LoadFaviconResult containing the bitmap(s) extracted from the downloaded file, or
* null if no or corrupt data was received.
* @throws IOException If attempts to fully read the stream result in such an exception, such as
* in the event of a transient connection failure.
* @throws URISyntaxException If the underlying call to tryDownload retries and raises such an
* exception trying a fallback URL.
*/
@VisibleForTesting
LoadFaviconResult downloadAndDecodeImage(Context context, String targetFaviconURL) throws IOException, URISyntaxException {
@Nullable
LoadFaviconResult downloadAndDecodeImage(Context context, String targetFaviconURL) {
// Try the URL we were given.
final HttpURLConnection connection = tryDownload(targetFaviconURL);
if (connection == null) {
@ -101,6 +100,9 @@ public class IconDownloader implements IconLoader {
try {
stream = connection.getInputStream();
return decodeImageFromResponse(context, stream, connection.getHeaderFieldInt("Content-Length", -1));
} catch (IOException e) {
Log.d(LOGTAG, "IOException while reading and decoding ixon", e);
return null;
} finally {
// Close the stream and free related resources.
IOUtils.safeStreamClose(stream);
@ -114,7 +116,8 @@ public class IconDownloader implements IconLoader {
* @param faviconURI URL of Favicon to try and download
* @return The HttpResponse containing the downloaded Favicon if successful, null otherwise.
*/
private HttpURLConnection tryDownload(String faviconURI) throws URISyntaxException, IOException {
@Nullable
private HttpURLConnection tryDownload(String faviconURI) {
final HashSet<String> visitedLinkSet = new HashSet<>();
visitedLinkSet.add(faviconURI);
return tryDownloadRecurse(faviconURI, visitedLinkSet);
@ -123,44 +126,54 @@ public class IconDownloader implements IconLoader {
/**
* Try to download from the favicon URL and recursively follow redirects.
*/
private HttpURLConnection tryDownloadRecurse(String faviconURI, HashSet<String> visited) throws URISyntaxException, IOException {
@Nullable
private HttpURLConnection tryDownloadRecurse(String faviconURI, HashSet<String> visited) {
if (visited.size() == MAX_REDIRECTS_TO_FOLLOW) {
return null;
}
final HttpURLConnection connection = connectTo(faviconURI);
HttpURLConnection connection = null;
// Was the response a failure?
final int status = connection.getResponseCode();
try {
connection = connectTo(faviconURI);
// Handle HTTP status codes requesting a redirect.
if (status >= 300 && status < 400) {
final String newURI = connection.getHeaderField("Location");
// Was the response a failure?
final int status = connection.getResponseCode();
// Handle mad web servers.
try {
if (newURI == null || newURI.equals(faviconURI)) {
return null;
// Handle HTTP status codes requesting a redirect.
if (status >= 300 && status < 400) {
final String newURI = connection.getHeaderField("Location");
// Handle mad web servers.
try {
if (newURI == null || newURI.equals(faviconURI)) {
return null;
}
if (visited.contains(newURI)) {
// Already been redirected here - abort.
return null;
}
visited.add(newURI);
} finally {
connection.disconnect();
}
if (visited.contains(newURI)) {
// Already been redirected here - abort.
return null;
}
visited.add(newURI);
} finally {
connection.disconnect();
return tryDownloadRecurse(newURI, visited);
}
return tryDownloadRecurse(newURI, visited);
}
if (status >= 400) {
// Client or Server error. Let's not retry loading from this URL again for some time.
FailureCache.get().rememberFailure(faviconURI);
if (status >= 400) {
// Client or Server error. Let's not retry loading from this URL again for some time.
FailureCache.get().rememberFailure(faviconURI);
connection.disconnect();
connection.disconnect();
return null;
}
} catch (IOException | URISyntaxException e) {
if (connection != null) {
connection.disconnect();
}
return null;
}
@ -168,9 +181,10 @@ public class IconDownloader implements IconLoader {
}
@VisibleForTesting
HttpURLConnection connectTo(String faviconURI) throws URISyntaxException, IOException {
@NonNull
HttpURLConnection connectTo(String uri) throws URISyntaxException, IOException {
final HttpURLConnection connection = (HttpURLConnection) ProxySelector.openConnectionWithProxy(
new URI(faviconURI));
new URI(uri));
connection.setRequestProperty("User-Agent", GeckoAppShell.getGeckoInterface().getDefaultUAString());
@ -179,8 +193,6 @@ public class IconDownloader implements IconLoader {
// redirects in loops forever.
connection.setInstanceFollowRedirects(false);
connection.connect();
return connection;
}
@ -195,6 +207,7 @@ public class IconDownloader implements IconLoader {
* @throws IOException If attempts to fully read the stream result in such an exception, such as
* in the event of a transient connection failure.
*/
@Nullable
private LoadFaviconResult decodeImageFromResponse(Context context, InputStream stream, int contentLength) throws IOException {
// This may not be provided, but if it is, it's useful.
final int bufferSize;

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

@ -4,6 +4,8 @@
package org.mozilla.gecko.media;
import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
@ -298,6 +300,15 @@ final class JellyBeanAsyncCodec implements AsyncCodec {
public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
assertCallbacks();
// Video decoder should config with adaptive playback capability.
if (surface != null) {
if (HardwareCodecCapabilityUtils.checkSupportsAdaptivePlayback(
mCodec, format.getString(MediaFormat.KEY_MIME))) {
// TODO: may need to find a way to not use hard code to decide the max w/h.
format.setInteger(MediaFormat.KEY_MAX_WIDTH, 1920);
format.setInteger(MediaFormat.KEY_MAX_HEIGHT, 1080);
}
}
mCodec.configure(format, surface, crypto, flags);
}

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

@ -5,708 +5,130 @@
package org.mozilla.gecko.tabs;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.tabs.TabsPanel.TabsLayout;
import org.mozilla.gecko.widget.themed.ThemedRelativeLayout;
import org.mozilla.gecko.widget.GridSpacingDecoration;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.GridView;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import java.util.ArrayList;
import java.util.List;
/**
* A tabs layout implementation for the tablet redesign (bug 1014156) and later ported to mobile (bug 1193745).
*/
class TabsGridLayout extends GridView
implements TabsLayout,
Tabs.OnTabsChangedListener {
import android.util.Log;
public class TabsGridLayout extends TabsLayout {
private static final String LOGTAG = "Gecko" + TabsGridLayout.class.getSimpleName();
public static final int ANIM_DELAY_MULTIPLE_MS = 20;
private static final int ANIM_TIME_MS = 200;
private static final DecelerateInterpolator ANIM_INTERPOLATOR = new DecelerateInterpolator();
private GridSpacingDecoration spacingDecoration;
private final int desiredItemWidth;
private final int desiredHorizontalItemSpacing;
private final int minHorizontalItemSpacing;
private final int verticalItemPadding;
private final SparseArray<PointF> tabLocations = new SparseArray<PointF>();
private final boolean isPrivate;
private final TabsLayoutAdapter tabsAdapter;
private final int columnWidth;
private TabsPanel tabsPanel;
private int lastSelectedTabId;
public TabsGridLayout(Context context, AttributeSet attrs) {
super(context, attrs, R.layout.tabs_layout_item_view);
public TabsGridLayout(final Context context, final AttributeSet attrs) {
super(context, attrs, R.attr.tabGridLayoutViewStyle);
final Resources resources = context.getResources();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsLayout);
isPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
a.recycle();
// Actual span count is updated in onSizeChanged.
setLayoutManager(new GridLayoutManager(context, 1));
tabsAdapter = new TabsGridLayoutAdapter(context);
setAdapter(tabsAdapter);
desiredItemWidth = resources.getDimensionPixelSize(R.dimen.tab_panel_item_width);
desiredHorizontalItemSpacing = resources.getDimensionPixelSize(R.dimen.tab_panel_grid_ideal_item_hspacing);
minHorizontalItemSpacing = resources.getDimensionPixelOffset(R.dimen.tab_panel_grid_min_item_hspacing);
verticalItemPadding = resources.getDimensionPixelSize(R.dimen.tab_panel_grid_item_vpadding);
final int viewPaddingHorizontal = resources.getDimensionPixelSize(R.dimen.tab_panel_grid_hpadding);
final int viewPaddingVertical = resources.getDimensionPixelSize(R.dimen.tab_panel_grid_vpadding);
setRecyclerListener(new RecyclerListener() {
@Override
public void onMovedToScrapHeap(View view) {
TabsLayoutItemView item = (TabsLayoutItemView) view;
item.setThumbnail(null);
}
});
// The clipToPadding setting in the styles.xml doesn't seem to be working (bug 1101784)
// so lets set it manually in code for the moment as it's needed for the padding animation
setPadding(viewPaddingHorizontal, viewPaddingVertical, viewPaddingHorizontal, viewPaddingVertical);
setClipToPadding(false);
setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);
setVerticalFadingEdgeEnabled(false);
setItemAnimator(new TabsGridLayoutAnimator());
final Resources resources = getResources();
columnWidth = resources.getDimensionPixelSize(R.dimen.tab_panel_column_width);
final int padding = resources.getDimensionPixelSize(R.dimen.tab_panel_grid_padding);
final int paddingTop = resources.getDimensionPixelSize(R.dimen.tab_panel_grid_padding_top);
// Lets set double the top padding on the bottom so that the last row shows up properly!
// Your demise, GridView, cannot come fast enough.
final int paddingBottom = paddingTop * 2;
setPadding(padding, paddingTop, padding, paddingBottom);
setOnItemClickListener(new OnItemClickListener() {
// A TouchHelper handler for swipe to close.
final TabsTouchHelperCallback callback = new TabsTouchHelperCallback(this) {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final TabsLayoutItemView tabView = (TabsLayoutItemView) view;
final int tabId = tabView.getTabId();
final Tab tab = Tabs.getInstance().selectTab(tabId);
if (tab == null) {
return;
}
autoHidePanel();
Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.OPENED_FROM_TABS_TRAY);
protected float alphaForItemSwipeDx(float dX, int distanceToAlphaMin) {
return 1f - 2f * Math.abs(dX) / distanceToAlphaMin;
}
});
TabSwipeGestureListener mSwipeListener = new TabSwipeGestureListener();
setOnTouchListener(mSwipeListener);
setOnScrollListener(mSwipeListener.makeScrollListener());
}
private void populateTabLocations(final Tab removedTab) {
tabLocations.clear();
final int firstPosition = getFirstVisiblePosition();
final int lastPosition = getLastVisiblePosition();
final int numberOfColumns = getNumColumns();
final int childCount = getChildCount();
final int removedPosition = tabsAdapter.getPositionForTab(removedTab);
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
final View child = getChildAt(i);
if (child != null) {
// Reset the transformations here in case the user is swiping tabs away fast and they swipe a tab
// before the last animation has finished (bug 1179195).
resetTransforms(child);
tabLocations.append(x, new PointF(child.getX(), child.getY()));
}
}
final boolean firstChildOffScreen = ((firstPosition > 0) || getChildAt(0).getY() < 0);
final boolean lastChildVisible = (lastPosition - childCount == firstPosition - 1);
final boolean oneItemOnLastRow = (lastPosition % numberOfColumns == 0);
if (firstChildOffScreen && lastChildVisible && oneItemOnLastRow) {
// We need to set the view's bottom padding to prevent a sudden jump as the
// last item in the row is being removed. We then need to remove the padding
// via a sweet animation
final int removedHeight = getChildAt(0).getMeasuredHeight();
final int verticalSpacing =
getResources().getDimensionPixelOffset(R.dimen.tab_panel_grid_vspacing);
ValueAnimator paddingAnimator = ValueAnimator.ofInt(getPaddingBottom() + removedHeight + verticalSpacing, getPaddingBottom());
paddingAnimator.setDuration(ANIM_TIME_MS * 2);
paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (Integer) animation.getAnimatedValue());
}
});
paddingAnimator.start();
}
}
@Override
public void setTabsPanel(TabsPanel panel) {
tabsPanel = panel;
}
@Override
public void show() {
setVisibility(View.VISIBLE);
Tabs.getInstance().refreshThumbnails();
Tabs.registerOnTabsChangedListener(this);
refreshTabsData();
final Tab currentlySelectedTab = Tabs.getInstance().getSelectedTab();
final int position = currentlySelectedTab != null ? tabsAdapter.getPositionForTab(currentlySelectedTab) : -1;
if (position != -1) {
final boolean selectionChanged = lastSelectedTabId != currentlySelectedTab.getId();
final boolean positionIsVisible = position >= getFirstVisiblePosition() && position <= getLastVisiblePosition();
if (selectionChanged || !positionIsVisible) {
smoothScrollToPosition(position);
}
}
}
@Override
public void hide() {
lastSelectedTabId = Tabs.getInstance().getSelectedTab().getId();
setVisibility(View.GONE);
Tabs.unregisterOnTabsChangedListener(this);
GeckoAppShell.notifyObservers("Tab:Screenshot:Cancel", "");
tabsAdapter.clear();
}
@Override
public boolean shouldExpand() {
return true;
}
private void autoHidePanel() {
tabsPanel.autoHidePanel();
}
@Override
public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
switch (msg) {
case ADDED:
// Refresh only if panel is shown. show() will call refreshTabsData() later again.
if (tabsPanel.isShown()) {
// Refresh the list to make sure the new tab is added in the right position.
refreshTabsData();
}
break;
case CLOSED:
// This is limited to >= ICS as animations on GB devices are generally pants
if (Build.VERSION.SDK_INT >= 11 && tabsAdapter.getCount() > 0) {
animateRemoveTab(tab);
}
final Tabs tabsInstance = Tabs.getInstance();
if (tabsAdapter.removeTab(tab)) {
if (tab.isPrivate() == isPrivate && tabsAdapter.getCount() > 0) {
int selected = tabsAdapter.getPositionForTab(tabsInstance.getSelectedTab());
updateSelectedStyle(selected);
}
if (!tab.isPrivate()) {
// Make sure we always have at least one normal tab
final Iterable<Tab> tabs = tabsInstance.getTabsInOrder();
boolean removedTabIsLastNormalTab = true;
for (Tab singleTab : tabs) {
if (!singleTab.isPrivate()) {
removedTabIsLastNormalTab = false;
break;
}
}
if (removedTabIsLastNormalTab) {
tabsInstance.addTab();
}
}
}
break;
case SELECTED:
// Update the selected position, then fall through...
updateSelectedPosition();
case UNSELECTED:
// We just need to update the style for the unselected tab...
case THUMBNAIL:
case TITLE:
case RECORDING_CHANGE:
case AUDIO_PLAYING_CHANGE:
View view = getChildAt(tabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
if (view == null)
return;
((TabsLayoutItemView) view).assignValues(tab);
break;
}
}
// Updates the selected position in the list so that it will be scrolled to the right place.
private void updateSelectedPosition() {
int selected = tabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
updateSelectedStyle(selected);
if (selected != -1) {
setSelection(selected);
}
}
/**
* Updates the selected/unselected style for the tabs.
*
* @param selected position of the selected tab
*/
private void updateSelectedStyle(final int selected) {
post(new Runnable() {
@Override
public void run() {
final int displayCount = tabsAdapter.getCount();
for (int i = 0; i < displayCount; i++) {
final Tab tab = tabsAdapter.getItem(i);
final boolean checked = displayCount == 1 || i == selected;
final View tabView = getViewForTab(tab);
if (tabView != null) {
((TabsLayoutItemView) tabView).setChecked(checked);
}
// setItemChecked doesn't exist until API 11, despite what the API docs say!
setItemChecked(i, checked);
}
}
});
}
private void refreshTabsData() {
// Store a different copy of the tabs, so that we don't have to worry about
// accidentally updating it on the wrong thread.
ArrayList<Tab> tabData = new ArrayList<>();
Iterable<Tab> allTabs = Tabs.getInstance().getTabsInOrder();
for (Tab tab : allTabs) {
if (tab.isPrivate() == isPrivate)
tabData.add(tab);
}
tabsAdapter.setTabs(tabData);
updateSelectedPosition();
}
private void resetTransforms(View view) {
view.setAlpha(1);
view.setTranslationX(0);
view.setTranslationY(0);
((TabsLayoutItemView) view).setCloseVisible(true);
};
final ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(this);
}
@Override
public void closeAll() {
autoHidePanel();
if (getChildCount() == 0) {
closeAllTabs();
}
@Override
protected boolean addAtIndexRequiresScroll(int index) {
final GridLayoutManager layoutManager = (GridLayoutManager) getLayoutManager();
final int spanCount = layoutManager.getSpanCount();
final int firstVisibleIndex = layoutManager.findFirstVisibleItemPosition();
// When you add an item at the first visible position to a GridLayoutManager and there's
// room to scroll, RecyclerView scrolls the new position to anywhere from near the bottom of
// its row to completely offscreen (for unknown reasons), so we need to scroll to fix that.
// We also scroll when the item being added is the only item on the final row.
return index == firstVisibleIndex ||
(index == getAdapter().getItemCount() - 1 && index % spanCount == 0);
}
private void updateSpacingDecoration(int horizontalItemSpacing) {
if (spacingDecoration != null) {
removeItemDecoration(spacingDecoration);
}
spacingDecoration = new GridSpacingDecoration(horizontalItemSpacing, verticalItemPadding);
addItemDecoration(spacingDecoration);
updateSelectedPosition();
}
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w == oldw) {
return;
}
final Iterable<Tab> tabs = Tabs.getInstance().getTabsInOrder();
for (Tab tab : tabs) {
// In the normal panel we want to close all tabs (both private and normal),
// but in the private panel we only want to close private tabs.
if (!isPrivate || tab.isPrivate()) {
Tabs.getInstance().closeTab(tab, false);
}
}
}
private View getViewForTab(Tab tab) {
final int position = tabsAdapter.getPositionForTab(tab);
return getChildAt(position - getFirstVisiblePosition());
}
void closeTab(View v) {
if (tabsAdapter.getCount() == 1) {
autoHidePanel();
}
TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
Tab tab = Tabs.getInstance().getTab(itemView.getTabId());
Tabs.getInstance().closeTab(tab, true);
}
private void animateRemoveTab(final Tab removedTab) {
final int removedPosition = tabsAdapter.getPositionForTab(removedTab);
final View removedView = getViewForTab(removedTab);
// The removed position might not have a matching child view
// when it's not within the visible range of positions in the strip.
if (removedView == null) {
return;
}
final int removedHeight = removedView.getMeasuredHeight();
populateTabLocations(removedTab);
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
// We don't animate the removed child view (it just disappears)
// but we still need its size to animate all affected children
// within the visible viewport.
final int childCount = getChildCount();
final int firstPosition = getFirstVisiblePosition();
final int numberOfColumns = getNumColumns();
final List<Animator> childAnimators = new ArrayList<>();
PropertyValuesHolder translateX, translateY;
for (int x = 0, i = removedPosition - firstPosition; i < childCount; i++, x++) {
final View child = getChildAt(i);
ObjectAnimator animator;
if (i % numberOfColumns == numberOfColumns - 1) {
// Animate X & Y
translateX = PropertyValuesHolder.ofFloat("translationX", -(columnWidth * numberOfColumns), 0);
translateY = PropertyValuesHolder.ofFloat("translationY", removedHeight, 0);
animator = ObjectAnimator.ofPropertyValuesHolder(child, translateX, translateY);
} else {
// Just animate X
translateX = PropertyValuesHolder.ofFloat("translationX", columnWidth, 0);
animator = ObjectAnimator.ofPropertyValuesHolder(child, translateX);
}
animator.setStartDelay(x * ANIM_DELAY_MULTIPLE_MS);
childAnimators.add(animator);
}
final AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(childAnimators);
animatorSet.setDuration(ANIM_TIME_MS);
animatorSet.setInterpolator(ANIM_INTERPOLATOR);
animatorSet.start();
// Set the starting position of the child views - because we are delaying the start
// of the animation, we need to prevent the items being drawn in their final position
// prior to the animation starting
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
final View child = getChildAt(i);
final PointF targetLocation = tabLocations.get(x + 1);
if (targetLocation == null) {
continue;
}
child.setX(targetLocation.x);
child.setY(targetLocation.y);
}
return true;
}
});
}
private void animateCancel(final View view) {
PropertyAnimator animator = new PropertyAnimator(ANIM_TIME_MS);
animator.attach(view, PropertyAnimator.Property.ALPHA, 1);
animator.attach(view, PropertyAnimator.Property.TRANSLATION_X, 0);
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
@Override
public void onPropertyAnimationStart() {
}
@Override
public void onPropertyAnimationEnd() {
TabsLayoutItemView tab = (TabsLayoutItemView) view;
tab.setCloseVisible(true);
}
});
animator.start();
}
private class TabsGridLayoutAdapter extends TabsLayoutAdapter {
final private Button.OnClickListener mCloseClickListener;
public TabsGridLayoutAdapter(Context context) {
super(context, R.layout.tabs_layout_item_view);
mCloseClickListener = new Button.OnClickListener() {
@Override
public void onClick(View v) {
closeTab(v);
}
};
}
@Override
TabsLayoutItemView newView(int position, ViewGroup parent) {
final TabsLayoutItemView item = super.newView(position, parent);
item.setCloseOnClickListener(mCloseClickListener);
((ThemedRelativeLayout) item.findViewById(R.id.wrapper)).setPrivateMode(isPrivate);
return item;
}
@Override
public void bindView(TabsLayoutItemView view, Tab tab) {
super.bindView(view, tab);
// If we're recycling this view, there's a chance it was transformed during
// the close animation. Remove any of those properties.
resetTransforms(view);
}
}
private class TabSwipeGestureListener implements View.OnTouchListener {
// same value the stock browser uses for after drag animation velocity in pixels/sec
// http://androidxref.com/4.0.4/xref/packages/apps/Browser/src/com/android/browser/NavTabScroller.java#61
private static final float MIN_VELOCITY = 750;
private final int mSwipeThreshold;
private final int mMinFlingVelocity;
private final int mMaxFlingVelocity;
private VelocityTracker mVelocityTracker;
private int mTabWidth = 1;
private View mSwipeView;
private Runnable mPendingCheckForTap;
private float mSwipeStartX;
private boolean mSwiping;
private boolean mEnabled;
public TabSwipeGestureListener() {
mEnabled = true;
ViewConfiguration vc = ViewConfiguration.get(TabsGridLayout.this.getContext());
mSwipeThreshold = vc.getScaledTouchSlop();
mMinFlingVelocity = (int) (TabsGridLayout.this.getContext().getResources().getDisplayMetrics().density * MIN_VELOCITY);
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
}
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
public OnScrollListener makeScrollListener() {
return new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
setEnabled(scrollState != GridView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
};
}
@Override
public boolean onTouch(View view, MotionEvent e) {
if (!mEnabled) {
return false;
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
// Check if we should set pressed state on the
// touched view after a standard delay.
triggerCheckForTap();
final float x = e.getRawX();
final float y = e.getRawY();
// Find out which view is being touched
mSwipeView = findViewAt(x, y);
if (mSwipeView != null) {
if (mTabWidth < 2) {
mTabWidth = mSwipeView.getWidth();
}
mSwipeStartX = e.getRawX();
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(e);
}
view.onTouchEvent(e);
return true;
}
case MotionEvent.ACTION_UP: {
if (mSwipeView == null) {
break;
}
cancelCheckForTap();
mSwipeView.setPressed(false);
if (!mSwiping) {
final TabsLayoutItemView item = (TabsLayoutItemView) mSwipeView;
final int tabId = item.getTabId();
final Tab tab = Tabs.getInstance().selectTab(tabId);
if (tab != null) {
autoHidePanel();
Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.OPENED_FROM_TABS_TRAY);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
break;
}
mVelocityTracker.addMovement(e);
mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
float velocityX = Math.abs(mVelocityTracker.getXVelocity());
boolean dismiss = false;
float deltaX = mSwipeView.getTranslationX();
if (Math.abs(deltaX) > mTabWidth / 2) {
dismiss = true;
} else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity) {
dismiss = mSwiping && (deltaX * mVelocityTracker.getYVelocity() > 0);
}
if (dismiss) {
closeTab(mSwipeView.findViewById(R.id.close));
} else {
animateCancel(mSwipeView);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
mSwipeView = null;
mSwipeStartX = 0;
mSwiping = false;
}
case MotionEvent.ACTION_MOVE: {
if (mSwipeView == null || mVelocityTracker == null) {
break;
}
mVelocityTracker.addMovement(e);
float delta = e.getRawX() - mSwipeStartX;
boolean isScrollingX = Math.abs(delta) > mSwipeThreshold;
boolean isSwipingToClose = isScrollingX;
// If we're actually swiping, make sure we don't
// set pressed state on the swiped view.
if (isScrollingX) {
cancelCheckForTap();
}
if (isSwipingToClose) {
mSwiping = true;
TabsGridLayout.this.requestDisallowInterceptTouchEvent(true);
((TabsLayoutItemView) mSwipeView).setCloseVisible(false);
// Stops listview from highlighting the touched item
// in the list when swiping.
MotionEvent cancelEvent = MotionEvent.obtain(e);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
(e.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
TabsGridLayout.this.onTouchEvent(cancelEvent);
cancelEvent.recycle();
}
if (mSwiping) {
mSwipeView.setTranslationX(delta);
mSwipeView.setAlpha(Math.min(1f, 1f - 2f * Math.abs(delta) / mTabWidth));
return true;
}
break;
}
}
return false;
}
private View findViewAt(float rawX, float rawY) {
Rect rect = new Rect();
int[] listViewCoords = new int[2];
TabsGridLayout.this.getLocationOnScreen(listViewCoords);
int x = (int) rawX - listViewCoords[0];
int y = (int) rawY - listViewCoords[1];
for (int i = 0; i < TabsGridLayout.this.getChildCount(); i++) {
View child = TabsGridLayout.this.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
return child;
}
}
return null;
}
private void triggerCheckForTap() {
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
TabsGridLayout.this.postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
}
private void cancelCheckForTap() {
if (mPendingCheckForTap == null) {
return;
}
TabsGridLayout.this.removeCallbacks(mPendingCheckForTap);
}
private class CheckForTap implements Runnable {
@Override
public void run() {
if (!mSwiping && mSwipeView != null && mEnabled) {
mSwipeView.setPressed(true);
final GridLayoutManager layoutManager = (GridLayoutManager) getLayoutManager();
final int nonPaddingWidth = w - getPaddingLeft() - getPaddingRight();
// We lay out the tabs so that the outer two tab edges are butted up against the
// RecyclerView padding, and then all other tab edges get their own padding, so
// nonPaddingWidth in terms of tab width w and tab spacing s for n tabs is
// n * w + (n - 1) * s
// Solving for n gives the formulas below.
final int idealSpacingSpanCount = Math.max(1,
(nonPaddingWidth + desiredHorizontalItemSpacing) / (desiredItemWidth + desiredHorizontalItemSpacing));
final int maxSpanCount = Math.max(1,
(nonPaddingWidth + minHorizontalItemSpacing) / (desiredItemWidth + minHorizontalItemSpacing));
// General caution note: span count can change here at a point where some ItemDecorations
// have been computed and some have not, and Android doesn't recompute ItemDecorations after
// a setSpanCount call, so we need to always remove and then add back our spacingDecoration
// (whose computations depend on spanCount) in order to get a full layout recompute.
if (idealSpacingSpanCount == maxSpanCount) {
layoutManager.setSpanCount(idealSpacingSpanCount);
updateSpacingDecoration(desiredHorizontalItemSpacing);
} else {
// We're gaining a column by decreasing the item spacing - this actually turns out to be
// necessary to fit three columns in landscape mode on many phones. It also allows us
// to match the span counts produced by the previous GridLayout implementation.
layoutManager.setSpanCount(maxSpanCount);
// Increase the spacing as much as we can without giving up our increased span count.
for (int spacing = minHorizontalItemSpacing + 1; spacing <= desiredHorizontalItemSpacing; spacing++) {
if (maxSpanCount * desiredItemWidth + (maxSpanCount - 1) * spacing > nonPaddingWidth) {
updateSpacingDecoration(spacing - 1);
return;
}
}
// We should never get here if our calculations above were correct.
Log.e(LOGTAG, "Span count calculation error");
updateSpacingDecoration(minHorizontalItemSpacing);
}
}
}

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

@ -0,0 +1,21 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.tabs;
import org.mozilla.gecko.widget.DefaultItemAnimatorBase;
import android.support.v7.widget.RecyclerView;
class TabsGridLayoutAnimator extends DefaultItemAnimatorBase {
public TabsGridLayoutAnimator() {
setSupportsChangeAnimations(false);
}
@Override
protected boolean preAnimateRemoveImpl(final RecyclerView.ViewHolder holder) {
return false;
}
}

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

@ -30,7 +30,7 @@ public abstract class TabsLayout extends RecyclerView
private final boolean isPrivate;
private TabsPanel tabsPanel;
private final TabsLayoutRecyclerAdapter tabsAdapter;
private final TabsLayoutAdapter tabsAdapter;
public TabsLayout(Context context, AttributeSet attrs, int itemViewLayoutResId) {
super(context, attrs);
@ -39,7 +39,7 @@ public abstract class TabsLayout extends RecyclerView
isPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
a.recycle();
tabsAdapter = new TabsLayoutRecyclerAdapter(context, itemViewLayoutResId, isPrivate,
tabsAdapter = new TabsLayoutAdapter(context, itemViewLayoutResId, isPrivate,
/* close on click listener */
new Button.OnClickListener() {
@Override
@ -101,8 +101,8 @@ public abstract class TabsLayout extends RecyclerView
final int tabIndex = Integer.parseInt(data);
tabsAdapter.notifyTabInserted(tab, tabIndex);
if (addAtIndexRequiresScroll(tabIndex)) {
// (The current Tabs implementation updates the SELECTED tab *after* this
// call to ADDED, so don't just call updateSelectedPosition().)
// (The SELECTED tab is updated *after* this call to ADDED, so don't just call
// updateSelectedPosition().)
scrollToPosition(tabIndex);
}
break;
@ -124,10 +124,16 @@ public abstract class TabsLayout extends RecyclerView
}
}
// Addition of a tab at selected positions (dependent on LayoutManager) will result in a tab
// being added out of view - return true if index is such a position.
/**
* Addition of a tab at selected positions (dependent on LayoutManager) will result in a tab
* being added out of view - return true if {@code index} is such a position.
*/
abstract protected boolean addAtIndexRequiresScroll(int index);
protected int getSelectedAdapterPosition() {
return tabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
}
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
final TabsLayoutItemView item = (TabsLayoutItemView) v;
@ -143,9 +149,9 @@ public abstract class TabsLayout extends RecyclerView
Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.OPENED_FROM_TABS_TRAY);
}
// Updates the selected position in the list so that it will be scrolled to the right place.
private void updateSelectedPosition() {
final int selected = tabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
/** Updates the selected position in the list so that it will be scrolled to the right place. */
protected void updateSelectedPosition() {
final int selected = getSelectedAdapterPosition();
if (selected != NO_POSITION) {
scrollToPosition(selected);
}
@ -199,6 +205,15 @@ public abstract class TabsLayout extends RecyclerView
closeTab(view);
}
@Override
public void onChildAttachedToWindow(View child) {
// Make sure we reset any attributes that may have been animated in this child's previous
// incarnation.
child.setTranslationX(0);
child.setTranslationY(0);
child.setAlpha(1);
}
private Tab getTabForView(View view) {
if (view == null) {
return null;

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

@ -5,96 +5,123 @@
package org.mozilla.gecko.tabs;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import java.util.ArrayList;
// Adapter to bind tabs into a list
public class TabsLayoutAdapter extends BaseAdapter {
public static final String LOGTAG = "Gecko" + TabsLayoutAdapter.class.getSimpleName();
public class TabsLayoutAdapter
extends RecyclerView.Adapter<TabsLayoutAdapter.TabsListViewHolder> {
private final Context mContext;
private final int mTabLayoutId;
private ArrayList<Tab> mTabs;
private final LayoutInflater mInflater;
private static final String LOGTAG = "Gecko" + TabsLayoutAdapter.class.getSimpleName();
public TabsLayoutAdapter (Context context, int tabLayoutId) {
mContext = context;
mInflater = LayoutInflater.from(mContext);
mTabLayoutId = tabLayoutId;
}
private final int tabLayoutId;
private @NonNull ArrayList<Tab> tabs;
private final LayoutInflater inflater;
private final boolean isPrivate;
// Click listener for the close button on itemViews.
private final Button.OnClickListener closeOnClickListener;
final void setTabs (ArrayList<Tab> tabs) {
mTabs = tabs;
notifyDataSetChanged(); // Be sure to call this whenever mTabs changes.
}
final boolean removeTab (Tab tab) {
boolean tabRemoved = mTabs.remove(tab);
if (tabRemoved) {
notifyDataSetChanged(); // Be sure to call this whenever mTabs changes.
// The TabsLayoutItemView takes care of caching its own Views, so we don't need to do anything
// here except not be abstract.
public static class TabsListViewHolder extends RecyclerView.ViewHolder {
public TabsListViewHolder(View itemView) {
super(itemView);
}
return tabRemoved;
}
final void clear() {
mTabs = null;
notifyDataSetChanged(); // Be sure to call this whenever mTabs changes.
public TabsLayoutAdapter(Context context, int tabLayoutId, boolean isPrivate,
Button.OnClickListener closeOnClickListener) {
inflater = LayoutInflater.from(context);
this.tabLayoutId = tabLayoutId;
this.isPrivate = isPrivate;
this.closeOnClickListener = closeOnClickListener;
tabs = new ArrayList<>(0);
}
@Override
public int getCount() {
return (mTabs == null ? 0 : mTabs.size());
/* package */ final void setTabs(@NonNull ArrayList<Tab> tabs) {
this.tabs = tabs;
notifyDataSetChanged();
}
@Override
public Tab getItem(int position) {
return mTabs.get(position);
/* package */ final void clear() {
tabs = new ArrayList<>(0);
notifyDataSetChanged();
}
@Override
public long getItemId(int position) {
return position;
}
final int getPositionForTab(Tab tab) {
if (mTabs == null || tab == null)
return -1;
return mTabs.indexOf(tab);
}
@Override
public boolean isEnabled(int position) {
/* package */ final boolean removeTab(Tab tab) {
final int position = getPositionForTab(tab);
if (position == -1) {
return false;
}
tabs.remove(position);
notifyItemRemoved(position);
return true;
}
@Override
final public TabsLayoutItemView getView(int position, View convertView, ViewGroup parent) {
final TabsLayoutItemView view;
if (convertView == null) {
view = newView(position, parent);
} else {
view = (TabsLayoutItemView) convertView;
/* package */ final int getPositionForTab(Tab tab) {
if (tab == null) {
return -1;
}
final Tab tab = mTabs.get(position);
bindView(view, tab);
return view;
return tabs.indexOf(tab);
}
TabsLayoutItemView newView(int position, ViewGroup parent) {
return (TabsLayoutItemView) mInflater.inflate(mTabLayoutId, parent, false);
/* package */ void notifyTabChanged(Tab tab) {
final int position = getPositionForTab(tab);
if (position != -1) {
notifyItemChanged(position);
}
}
void bindView(TabsLayoutItemView view, Tab tab) {
view.assignValues(tab);
/* package */ void notifyTabInserted(Tab tab, int index) {
if (index >= 0 && index <= tabs.size()) {
tabs.add(index, tab);
notifyItemInserted(index);
} else {
// Add to the end.
tabs.add(tab);
notifyItemInserted(tabs.size() - 1);
// index == -1 is a valid way to add to the end, the other cases are errors.
if (index != -1) {
Log.e(LOGTAG, "Tab was inserted at an invalid position: " + Integer.toString(index));
}
}
}
}
@Override
public int getItemCount() {
return tabs.size();
}
private Tab getItem(int position) {
return tabs.get(position);
}
@Override
public void onBindViewHolder(TabsListViewHolder viewHolder, int position) {
final Tab tab = getItem(position);
final TabsLayoutItemView itemView = (TabsLayoutItemView) viewHolder.itemView;
itemView.assignValues(tab);
// Be careful (re)setting position values here: bind is called on each notifyItemChanged,
// so you could be stomping on values that have been set in support of other animations
// that are already underway.
}
@Override
public TabsListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final TabsLayoutItemView viewItem = (TabsLayoutItemView) inflater.inflate(tabLayoutId, parent, false);
viewItem.setPrivateMode(isPrivate);
viewItem.setCloseOnClickListener(closeOnClickListener);
return new TabsListViewHolder(viewItem);
}
}

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

@ -32,7 +32,13 @@ public class TabsListLayout extends TabsLayout {
setLayoutManager(new LinearLayoutManager(context));
// A TouchHelper handler for swipe to close.
final TabsTouchHelperCallback callback = new TabsTouchHelperCallback(this);
final TabsTouchHelperCallback callback = new TabsTouchHelperCallback(this) {
@Override
protected float alphaForItemSwipeDx(float dX, int distanceToAlphaMin) {
return Math.max(0.1f,
Math.min(1f, 1f - 2f * Math.abs(dX) / distanceToAlphaMin));
}
};
final ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(this);
@ -106,13 +112,4 @@ public class TabsListLayout extends TabsLayout {
protected boolean addAtIndexRequiresScroll(int index) {
return index == 0 || index == getAdapter().getItemCount() - 1;
}
@Override
public void onChildAttachedToWindow(View child) {
// Make sure we reset any attributes that may have been animated in this child's previous
// incarnation.
child.setTranslationX(0);
child.setTranslationY(0);
child.setAlpha(1);
}
}

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

@ -10,7 +10,7 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View;
class TabsTouchHelperCallback extends ItemTouchHelper.Callback {
abstract class TabsTouchHelperCallback extends ItemTouchHelper.Callback {
private final DismissListener dismissListener;
interface DismissListener {
@ -42,8 +42,16 @@ class TabsTouchHelperCallback extends ItemTouchHelper.Callback {
return false;
}
// Alpha on an itemView being swiped should decrease to a min over a distance equal to the
// width of the item being swiped.
/**
* Returns the alpha an itemView should be set to when swiped by an amount {@code dX}, given
* that alpha should decrease to its min at distance {@code distanceToAlphaMin}.
*/
abstract protected float alphaForItemSwipeDx(float dX, int distanceToAlphaMin);
/**
* Alpha on an itemView being swiped should decrease to a min over a distance equal to the
* width of the item being swiped.
*/
@Override
public void onChildDraw(Canvas c,
RecyclerView recyclerView,
@ -58,8 +66,7 @@ class TabsTouchHelperCallback extends ItemTouchHelper.Callback {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
viewHolder.itemView.setAlpha(Math.max(0.1f,
Math.min(1f, 1f - 2f * Math.abs(dX) / viewHolder.itemView.getWidth())));
viewHolder.itemView.setAlpha(alphaForItemSwipeDx(dX, viewHolder.itemView.getWidth()));
}
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

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

@ -94,32 +94,6 @@ final class UnusedResourcesUtil {
R.xml.preferences_default_browser_tablet
};
// We are migrating to Gradle 2.10 and the Android Gradle plugin 2.0. The new plugin does find
// more unused resources but we are not ready to remove them yet. Some of the resources are going
// to be reused soon. This is a temporary solution so that the gradle migration is not blocked.
// See bug 1263390 / bug 1268414.
public static final int[] TEMPORARY_UNUSED_WHILE_MIGRATING_GRADLE = {
R.color.remote_tabs_setup_button_background_hit,
R.drawable.remote_tabs_setup_button_background,
R.style.TabsPanelSectionBase,
R.style.TabsPanelSection,
R.style.TabsPanelItemBase,
R.style.TabsPanelItem,
R.style.TabsPanelItem_TextAppearance,
R.style.TabsPanelItem_TextAppearance_Header,
R.style.TabsPanelItem_TextAppearance_Linkified,
R.style.TabWidget,
R.style.GeckoDialogTitle,
R.style.GeckoDialogTitle_SubTitle,
R.style.RemoteTabsPanelItem,
R.style.RemoteTabsPanelItem_TextAppearance,
R.style.RemoteTabsPanelItem_TextAppearance_Header,
R.style.RemoteTabsPanelItem_TextAppearance_Linkified,
R.style.RemoteTabsPanelItem_Button,
};
// String resources that are used in the full-pane Activity Stream that are temporarily
// not needed while Activity Stream is part of the HomePager
public static final int[] TEMPORARY_UNUSED_ACTIVITY_STREAM = {

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

@ -0,0 +1,48 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.widget;
import android.graphics.Rect;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* An ItemDecoration for a GridLayoutManager that provides fixed spacing (but not fixed padding)
* to create fixed sized items, with no spacing on the outer edges of the outer items.
* <p>
* So, for example, if there are 2 columns and the spacing is s, then the first column gets a right
* padding of s/2 and the second column gets a left paddding of s/2. If there are three columns
* then the first column gets a right padding of 2s/3, the second column gets left and right
* paddings of s/3, and the third column gets a left padding of 2s/3.
* </p>
*/
public class GridSpacingDecoration extends RecyclerView.ItemDecoration {
private final int horizontalSpacing;
private final int verticalPadding;
public GridSpacingDecoration(int horizontalSpacing, int verticalPadding) {
this.horizontalSpacing = horizontalSpacing;
this.verticalPadding = verticalPadding;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final int position = parent.getChildAdapterPosition(view);
if (position == RecyclerView.NO_POSITION) {
return;
}
final GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
final int spanCount = layoutManager.getSpanCount();
final int column = position % spanCount;
final int columnLeftOffset = (int) (((float) column / (float) spanCount) * horizontalSpacing);
final int columnRightOffset = (int) (((float) (spanCount - (column + 1)) / (float) spanCount) * horizontalSpacing);
outRect.set(columnLeftOffset, verticalPadding, columnRightOffset, verticalPadding);
}
}

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

@ -685,10 +685,10 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'tabs/TabHistoryPage.java',
'tabs/TabPanelBackButton.java',
'tabs/TabsGridLayout.java',
'tabs/TabsGridLayoutAnimator.java',
'tabs/TabsLayout.java',
'tabs/TabsLayoutAdapter.java',
'tabs/TabsLayoutItemView.java',
'tabs/TabsLayoutRecyclerAdapter.java',
'tabs/TabsListLayout.java',
'tabs/TabsListLayoutAnimator.java',
'tabs/TabsPanel.java',
@ -770,6 +770,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'widget/FlowLayout.java',
'widget/GeckoActionProvider.java',
'widget/GeckoPopupMenu.java',
'widget/GridSpacingDecoration.java',
'widget/HistoryDividerItemDecoration.java',
'widget/IconTabWidget.java',
'widget/LoginDoorHanger.java',

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

@ -7,12 +7,12 @@
xmlns:gecko="http://schemas.android.com/apk/res-auto"
style="@style/TabsItem"
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<LinearLayout android:layout_width="fill_parent"
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:duplicateParentState="true"
@ -60,8 +60,8 @@
android:clipToPadding="false">
<org.mozilla.gecko.tabs.TabsPanelThumbnailView android:id="@+id/thumbnail"
android:layout_width="@dimen/tab_thumbnail_width"
android:layout_height="@dimen/tab_thumbnail_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="2dp"
android:outlineProvider="bounds"
/>

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

@ -7,5 +7,5 @@
<!-- Remote Tabs static view top padding. Less in landscape on phones. -->
<dimen name="home_remote_tabs_top_padding">16dp</dimen>
<dimen name="tab_panel_grid_padding">48dp</dimen>
<dimen name="tab_panel_grid_hpadding">48dp</dimen>
</resources>

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

@ -18,15 +18,4 @@
<item name="android:nextFocusUp">@+id/info</item>
</style>
<!-- Tabs panel -->
<style name="TabsPanelSection" parent="TabsPanelSectionBase">
<item name="android:layout_weight">1</item>
</style>
<style name="TabsPanelItem">
<item name="android:layout_marginBottom">20dp</item>
<item name="android:layout_gravity">left</item>
<item name="android:gravity">left</item>
</style>
</resources>

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

@ -21,14 +21,4 @@
<item name="android:verticalSpacing">20dp</item>
</style>
<!-- Tabs panel -->
<style name="TabsPanelSection" parent="TabsPanelSectionBase">
<item name="android:layout_marginLeft">20dp</item>
<item name="android:layout_marginRight">20dp</item>
</style>
<style name="TabsPanelItem" parent="TabsPanelItemBase">
<!-- To override the values-land style. -->
</style>
</resources>

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

@ -4,7 +4,7 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<resources>
<dimen name="tab_panel_column_width">143dip</dimen>
<dimen name="tab_panel_item_width">143dip</dimen>
<dimen name="tab_thumbnail_height">100dip</dimen>
<dimen name="tab_thumbnail_width">135dip</dimen>
</resources>

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

@ -4,7 +4,7 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<resources>
<dimen name="tab_panel_column_width">156dip</dimen>
<dimen name="tab_panel_item_width">156dip</dimen>
<dimen name="tab_thumbnail_height">110dip</dimen>
<dimen name="tab_thumbnail_width">148dip</dimen>

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

@ -4,7 +4,7 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<resources>
<dimen name="tab_panel_column_width">176dip</dimen>
<dimen name="tab_panel_item_width">176dip</dimen>
<dimen name="tab_thumbnail_height">120dip</dimen>
<dimen name="tab_thumbnail_width">168dip</dimen>
</resources>

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

@ -39,7 +39,6 @@
<item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
<item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
<item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
<item name="tabGridLayoutViewStyle">@style/Widget.TabsGridLayout</item>
</style>
</resources>

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

@ -5,11 +5,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="GeckoDialogTitle">
<!-- Override this to use a Holo theme on v13+ -->
<item name="android:textAppearance">@android:style/TextAppearance.Holo.DialogWindowTitle</item>
</style>
<style name="TextAppearance.Widget.ActionBar.Title" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title"/>
</resources>

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

@ -34,7 +34,6 @@
<item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
<item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
<item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
<item name="tabGridLayoutViewStyle">@style/Widget.TabsGridLayout</item>
</style>
</resources>

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

@ -5,6 +5,6 @@
<resources>
<dimen name="tab_panel_grid_padding">64dp</dimen>
<dimen name="tab_panel_grid_hpadding">64dp</dimen>
</resources>

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

@ -6,6 +6,6 @@
<resources>
<dimen name="panel_grid_view_column_width">250dp</dimen>
<dimen name="tab_panel_grid_padding">48dp</dimen>
<dimen name="tab_panel_grid_hpadding">48dp</dimen>
</resources>

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

@ -10,12 +10,6 @@
Please refer to values/styles.xml for default styles.
-->
<!-- TabWidget -->
<style name="TabWidget">
<item name="android:layout_width">300dip</item>
<item name="android:layout_height">48dip</item>
</style>
<style name="Widget.TopSitesListView" parent="Widget.BookmarksListView">
<item name="android:paddingTop">30dp</item>
<item name="android:paddingLeft">32dp</item>

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

@ -34,9 +34,6 @@
<!-- Styles for dynamic panel grid views -->
<attr name="panelIconViewStyle" format="reference" />
<!-- Style for the TabsGridLayout -->
<attr name="tabGridLayoutViewStyle" format="reference" />
<!-- Default style for the TopSitesGridView -->
<attr name="topSitesGridViewStyle" format="reference" />

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

@ -144,10 +144,12 @@
<dimen name="tab_thumbnail_width">121dp</dimen>
<dimen name="tab_thumbnail_height">90dp</dimen>
<dimen name="tab_panel_column_width">129dp</dimen>
<dimen name="tab_panel_grid_padding">20dp</dimen>
<dimen name="tab_panel_grid_vspacing">20dp</dimen>
<dimen name="tab_panel_grid_padding_top">19dp</dimen>
<dimen name="tab_panel_item_width">129dp</dimen>
<dimen name="tab_panel_grid_hpadding">20dp</dimen>
<dimen name="tab_panel_grid_vpadding">19dp</dimen>
<dimen name="tab_panel_grid_ideal_item_hspacing">20dp</dimen>
<dimen name="tab_panel_grid_min_item_hspacing">2dp</dimen>
<dimen name="tab_panel_grid_item_vpadding">10dp</dimen>
<dimen name="tab_highlight_stroke_width">4dp</dimen>

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

@ -180,21 +180,6 @@
<item name="android:orientation">vertical</item>
</style>
<style name="Widget.TabsGridLayout" parent="Widget.GridView">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:paddingTop">0dp</item>
<item name="android:stretchMode">spacingWidth</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
<item name="android:gravity">center</item>
<item name="android:numColumns">auto_fit</item>
<item name="android:columnWidth">@dimen/tab_panel_column_width</item>
<item name="android:horizontalSpacing">2dp</item>
<item name="android:verticalSpacing">@dimen/tab_panel_grid_vspacing</item>
<item name="android:drawSelectorOnTop">true</item>
<item name="android:clipToPadding">false</item>
</style>
<style name="Widget.BookmarkItemView" parent="Widget.TwoLinePageRow"/>
<style name="Widget.BookmarksListView" parent="Widget.HomeListView"/>
@ -493,44 +478,6 @@
<item name="android:nextFocusLeft">@+id/info</item>
</style>
<!-- Tabs panel -->
<style name="TabsPanelSectionBase">
<item name="android:orientation">vertical</item>
<item name="android:layout_marginLeft">40dp</item>
<item name="android:layout_marginRight">40dp</item>
</style>
<style name="TabsPanelSection" parent="TabsPanelSectionBase">
<!-- We set values in landscape. -->
</style>
<style name="TabsPanelItemBase">
<item name="android:layout_marginBottom">28dp</item>
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center</item>
</style>
<style name="TabsPanelItem" parent="TabsPanelItemBase">
<!-- We set values in landscape. -->
</style>
<style name="TabsPanelItem.TextAppearance">
<item name="android:textColor">#C0C9D0</item>
<item name="android:textSize">14sp</item>
<item name="android:lineSpacingMultiplier">1.35</item>
</style>
<style name="TabsPanelItem.TextAppearance.Header">
<item name="android:textSize">18sp</item>
<item name="android:layout_marginBottom">16dp</item>
</style>
<style name="TabsPanelItem.TextAppearance.Linkified">
<item name="android:clickable">true</item>
<item name="android:focusable">true</item>
<item name="android:textColor">#0292D6</item>
</style>
<style name="Widget.RemoteTabsItemView" parent="Widget.TwoLinePageRow"/>
<style name="Widget.RemoteTabsClientView" parent="Widget.TwoLinePageRow">
@ -558,13 +505,6 @@
<item name="android:textColor">#FFA4A7A9</item>
</style>
<!-- TabWidget -->
<style name="TabWidget">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">40dip</item>
<item name="android:layout_weight">1.0</item>
</style>
<!-- Find bar -->
<style name="FindBar">
<item name="android:background">@color/text_and_tabs_tray_grey</item>
@ -585,12 +525,6 @@
<item name="android:background">@drawable/action_bar_button_inverse</item>
</style>
<style name="GeckoDialogTitle">
<item name="android:textAppearance">@android:style/TextAppearance.DialogWindowTitle</item>
</style>
<style name="GeckoDialogTitle.SubTitle" />
<style name="PopupAnimation">
<item name="@android:windowEnterAnimation">@anim/popup_show</item>
<item name="@android:windowExitAnimation">@anim/popup_hide</item>

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

@ -95,7 +95,6 @@
<item name="android:spinnerStyle">@style/Widget.Spinner</item>
<item name="android:windowBackground">@android:color/white</item>
<item name="bookmarksListViewStyle">@style/Widget.BookmarksListView</item>
<item name="tabGridLayoutViewStyle">@style/Widget.TabsGridLayout</item>
<item name="geckoMenuListViewStyle">@style/Widget.GeckoMenuListView</item>
<item name="homeListViewStyle">@style/Widget.HomeListView</item>
<item name="menuItemActionBarStyle">@style/Widget.MenuItemActionBar</item>

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

@ -9,6 +9,7 @@ package org.mozilla.gecko.util;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.AppConstants.Versions;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecList;
@ -53,6 +54,25 @@ public final class HardwareCodecCapabilityUtils {
return false;
}
@WrapForJNI
public static boolean checkSupportsAdaptivePlayback(MediaCodec aCodec, String aMimeType) {
// isFeatureSupported supported on API level >= 19.
if (!Versions.feature19Plus) {
return false;
}
try {
MediaCodecInfo info = aCodec.getCodecInfo();
MediaCodecInfo.CodecCapabilities capabilities = info.getCapabilitiesForType(aMimeType);
return capabilities != null &&
capabilities.isFeatureSupported(
MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback);
} catch (IllegalArgumentException e) {
Log.e(LOGTAG, "Retrieve codec information failed", e);
}
return false;
}
public static boolean getHWEncoderCapability() {
if (Versions.feature20Plus) {
for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {

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

@ -16,11 +16,13 @@ import org.mozilla.gecko.icons.Icons;
import org.mozilla.gecko.icons.storage.FailureCache;
import org.robolectric.RuntimeEnvironment;
import java.io.IOException;
import java.net.HttpURLConnection;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@ -109,4 +111,32 @@ public class TestIconDownloader {
Assert.assertTrue(FailureCache.get().isKnownFailure(faviconUrl));
}
/**
* Scenario: Connected to successfully to server but reading the response code throws an exception.
*
* Verify that:
* * disconnect() is called on HttpUrlConnection
*/
@Test
public void testConnectionIsClosedWhenReadingResponseCodeThrows() throws Exception {
final IconRequest request = Icons.with(RuntimeEnvironment.application)
.pageUrl("http://www.mozilla.org")
.icon(IconDescriptor.createFavicon(
"https://www.mozilla.org/media/img/favicon.52506929be4c.ico",
32,
"image/x-icon"))
.build();
HttpURLConnection mockedConnection = mock(HttpURLConnection.class);
doThrow(new IOException()).when(mockedConnection).getResponseCode();
final IconDownloader downloader = spy(new IconDownloader());
doReturn(mockedConnection).when(downloader).connectTo(anyString());
IconResponse response = downloader.load(request);
Assert.assertNull(response);
verify(mockedConnection).disconnect();
}
}

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

@ -612,54 +612,15 @@ abstract class BaseTest extends BaseRobocopTest {
}
}
// A temporary tabs list/grid holder while the list and grid views are being transitioned to
// RecyclerViews (bug 1116415 and bug 1310081).
private static class TabsView {
private AdapterView<ListAdapter> gridView;
private RecyclerView listView;
public TabsView(View view) {
if (view instanceof RecyclerView) {
listView = (RecyclerView) view;
} else {
gridView = (AdapterView<ListAdapter>) view;
}
}
public void bringPositionIntoView(int index) {
if (gridView != null) {
gridView.setSelection(index);
} else {
listView.scrollToPosition(index);
}
}
public View getViewAtIndex(int index) {
if (gridView != null) {
return gridView.getChildAt(index - gridView.getFirstVisiblePosition());
} else {
final RecyclerView.ViewHolder itemViewHolder = listView.findViewHolderForLayoutPosition(index);
return itemViewHolder == null ? null : itemViewHolder.itemView;
}
}
public void post(Runnable runnable) {
if (gridView != null) {
gridView.post(runnable);
} else {
listView.post(runnable);
}
}
}
/**
* Gets the AdapterView of the tabs list.
* Gets the RecyclerView of the tabs list.
*
* @return List view in the tabs panel
*/
private final TabsView getTabsLayout() {
private final RecyclerView getTabsLayout() {
Element tabs = mDriver.findElement(getActivity(), R.id.tabs);
tabs.click();
return new TabsView(getActivity().findViewById(R.id.normal_tabs));
return (RecyclerView) getActivity().findViewById(R.id.normal_tabs);
}
/**
@ -670,12 +631,12 @@ abstract class BaseTest extends BaseRobocopTest {
private View getTabViewAt(final int index) {
final View[] childView = { null };
final TabsView view = getTabsLayout();
final RecyclerView view = getTabsLayout();
runOnUiThreadSync(new Runnable() {
@Override
public void run() {
view.bringPositionIntoView(index);
view.scrollToPosition(index);
// The selection isn't updated synchronously; posting a
// runnable to the view's queue guarantees we'll run after the
@ -684,7 +645,9 @@ abstract class BaseTest extends BaseRobocopTest {
@Override
public void run() {
// Index is relative to all views in the list.
childView[0] = view.getViewAtIndex(index);
final RecyclerView.ViewHolder itemViewHolder =
view.findViewHolderForLayoutPosition(index);
childView[0] = itemViewHolder == null ? null : itemViewHolder.itemView;
}
});
}

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

@ -9,6 +9,7 @@ interface nsIInterfaceRequestor;
interface nsINetAddr;
%{ C++
#include "mozilla/BasePrincipal.h"
namespace mozilla {
namespace net {
union NetAddr;
@ -17,6 +18,8 @@ union NetAddr;
%}
native NetAddr(mozilla::net::NetAddr);
[ptr] native NetAddrPtr(mozilla::net::NetAddr);
native NeckoOriginAttributes(mozilla::NeckoOriginAttributes);
[ref] native const_OriginAttributesRef(const mozilla::NeckoOriginAttributes);
/**
* nsISocketTransport
@ -45,12 +48,20 @@ interface nsISocketTransport : nsITransport
readonly attribute long port;
/**
* This is only non-empty when "privacy.firstparty.isolate" is enabled.
* It is used to create sockets, and will eventually be used to isolate
* OCSP cache. It's the only way to carry it down to NSPR layers which are
* final consumers. It must be set before the socket transport is built.
* The origin attributes are used to create sockets. The first party domain
* will eventually be used to isolate OCSP cache and is only non-empty when
* "privacy.firstparty.isolate" is enabled. Setting this is the only way to
* carry origin attributes down to NSPR layers which are final consumers.
* It must be set before the socket transport is built.
*/
attribute AUTF8String firstPartyDomain;
[implicit_jscontext, binaryname(ScriptableOriginAttributes)]
attribute jsval originAttributes;
[noscript, nostdcall, binaryname(GetOriginAttributes)]
NeckoOriginAttributes binaryGetOriginAttributes();
[noscript, nostdcall, binaryname(SetOriginAttributes)]
void binarySetOriginAttributes(in const_OriginAttributesRef aOriginAttrs);
/**
* The platform-specific network interface id that this socket

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

@ -22,6 +22,7 @@
#include "prerr.h"
#include "NetworkActivityMonitor.h"
#include "NSSErrorsService.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/net/NeckoChild.h"
#include "nsThreadUtils.h"
#include "nsISocketProviderService.h"
@ -1169,7 +1170,7 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &us
rv = provider->NewSocket(mNetAddr.raw.family,
mHttpsProxy ? mProxyHost.get() : host,
mHttpsProxy ? mProxyPort : port,
proxyInfo, mFirstPartyDomain,
proxyInfo, mOriginAttributes,
controlFlags, &fd,
getter_AddRefs(secinfo));
@ -1184,7 +1185,7 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &us
// to the stack (such as pushing an io layer)
rv = provider->AddToSocket(mNetAddr.raw.family,
host, port, proxyInfo,
mFirstPartyDomain, controlFlags, fd,
mOriginAttributes, controlFlags, fd,
getter_AddRefs(secinfo));
}
@ -2391,19 +2392,46 @@ nsSocketTransport::SetNetworkInterfaceId(const nsACString_internal &aNetworkInte
}
NS_IMETHODIMP
nsSocketTransport::GetFirstPartyDomain(nsACString &value)
nsSocketTransport::GetScriptableOriginAttributes(JSContext* aCx,
JS::MutableHandle<JS::Value> aOriginAttributes)
{
value = mFirstPartyDomain;
if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aOriginAttributes))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetFirstPartyDomain(const nsACString &value)
nsSocketTransport::SetScriptableOriginAttributes(JSContext* aCx,
JS::Handle<JS::Value> aOriginAttributes)
{
MutexAutoLock lock(mLock);
NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
mFirstPartyDomain = value;
NeckoOriginAttributes attrs;
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
mOriginAttributes = attrs;
return NS_OK;
}
nsresult
nsSocketTransport::GetOriginAttributes(NeckoOriginAttributes* aOriginAttributes)
{
NS_ENSURE_ARG(aOriginAttributes);
*aOriginAttributes = mOriginAttributes;
return NS_OK;
}
nsresult
nsSocketTransport::SetOriginAttributes(const NeckoOriginAttributes& aOriginAttributes)
{
MutexAutoLock lock(mLock);
NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
mOriginAttributes = aOriginAttributes;
return NS_OK;
}

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

@ -303,11 +303,12 @@ private:
bool mHttpsProxy;
uint32_t mConnectionFlags;
// This is only non-empty when "privacy.firstparty.isolate" is enabled.
// It is used to create sockets. It's the only way to carry it down to NSPR
// layers which are final consumers. It must be set before the socket
// transport is built.
nsCString mFirstPartyDomain;
// The origin attributes are used to create sockets. The first party domain
// will eventually be used to isolate OCSP cache and is only non-empty when
// "privacy.firstparty.isolate" is enabled. Setting this is the only way to
// carry origin attributes down to NSPR layers which are final consumers.
// It must be set before the socket transport is built.
NeckoOriginAttributes mOriginAttributes;
uint16_t SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; }
const nsCString &SocketHost() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyHost : mHost; }

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

@ -77,8 +77,9 @@ TLSFilterTransaction::TLSFilterTransaction(nsAHttpTransaction *aWrapped,
if (provider && mFD) {
mFD->secret = reinterpret_cast<PRFilePrivate *>(this);
provider->AddToSocket(PR_AF_INET, aTLSHost, aTLSPort, nullptr, EmptyCString(),
0, mFD, getter_AddRefs(mSecInfo));
provider->AddToSocket(PR_AF_INET, aTLSHost, aTLSPort, nullptr,
NeckoOriginAttributes(), 0, mFD,
getter_AddRefs(mSecInfo));
}
if (mTransaction) {
@ -1593,8 +1594,32 @@ FWD_TS_PTR(GetConnectionFlags, uint32_t);
FWD_TS(SetConnectionFlags, uint32_t);
FWD_TS_PTR(GetRecvBufferSize, uint32_t);
FWD_TS(SetRecvBufferSize, uint32_t);
FWD_TS(SetFirstPartyDomain, const nsACString&);
FWD_TS(GetFirstPartyDomain, nsACString&);
nsresult
SocketTransportShim::GetOriginAttributes(mozilla::NeckoOriginAttributes* aOriginAttributes)
{
return mWrapped->GetOriginAttributes(aOriginAttributes);
}
nsresult
SocketTransportShim::SetOriginAttributes(const mozilla::NeckoOriginAttributes& aOriginAttributes)
{
return mWrapped->SetOriginAttributes(aOriginAttributes);
}
NS_IMETHODIMP
SocketTransportShim::GetScriptableOriginAttributes(JSContext* aCx,
JS::MutableHandle<JS::Value> aOriginAttributes)
{
return mWrapped->GetScriptableOriginAttributes(aCx, aOriginAttributes);
}
NS_IMETHODIMP
SocketTransportShim::SetScriptableOriginAttributes(JSContext* aCx,
JS::Handle<JS::Value> aOriginAttributes)
{
return mWrapped->SetScriptableOriginAttributes(aCx, aOriginAttributes);
}
NS_IMETHODIMP
SocketTransportShim::GetHost(nsACString & aHost)

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

@ -3081,10 +3081,10 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
socketTransport->SetConnectionFlags(tmpFlags);
nsAutoCString firstPartyDomain =
NS_ConvertUTF16toUTF8(mEnt->mConnInfo->GetOriginAttributes().mFirstPartyDomain);
if (!firstPartyDomain.IsEmpty()) {
socketTransport->SetFirstPartyDomain(firstPartyDomain);
NeckoOriginAttributes originAttributes =
mEnt->mConnInfo->GetOriginAttributes();
if (originAttributes != NeckoOriginAttributes()) {
socketTransport->SetOriginAttributes(originAttributes);
}
socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());

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

@ -7,6 +7,12 @@
interface nsIProxyInfo;
[ptr] native PRFileDescStar(struct PRFileDesc);
native NeckoOriginAttributes(mozilla::NeckoOriginAttributes);
[ref] native const_OriginAttributesRef(const mozilla::NeckoOriginAttributes);
%{ C++
#include "mozilla/BasePrincipal.h"
%}
/**
* nsISocketProvider
@ -36,14 +42,14 @@ interface nsISocketProvider : nsISupports
* object typically implements nsITransportSecurityInfo.
*/
[noscript]
void newSocket(in long aFamily,
in string aHost,
in long aPort,
in nsIProxyInfo aProxy,
in ACString aFirstPartyDomain,
in unsigned long aFlags,
out PRFileDescStar aFileDesc,
out nsISupports aSecurityInfo);
void newSocket(in long aFamily,
in string aHost,
in long aPort,
in nsIProxyInfo aProxy,
in const_OriginAttributesRef aOriginAttributes,
in unsigned long aFlags,
out PRFileDescStar aFileDesc,
out nsISupports aSecurityInfo);
/**
* addToSocket
@ -56,14 +62,14 @@ interface nsISocketProvider : nsISupports
* which is an in-param instead.
*/
[noscript]
void addToSocket(in long aFamily,
in string aHost,
in long aPort,
in nsIProxyInfo aProxy,
in ACString aFirstPartyDomain,
in unsigned long aFlags,
in PRFileDescStar aFileDesc,
out nsISupports aSecurityInfo);
void addToSocket(in long aFamily,
in string aHost,
in long aPort,
in nsIProxyInfo aProxy,
in const_OriginAttributesRef aOriginAttributes,
in unsigned long aFlags,
in PRFileDescStar aFileDesc,
out nsISupports aSecurityInfo);
/**
* PROXY_RESOLVES_HOST

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

@ -11,6 +11,8 @@
#include "nsCOMPtr.h"
#include "nsError.h"
using mozilla::NeckoOriginAttributes;
//////////////////////////////////////////////////////////////////////////
NS_IMPL_ISUPPORTS(nsSOCKSSocketProvider, nsISocketProvider)
@ -46,7 +48,7 @@ nsSOCKSSocketProvider::NewSocket(int32_t family,
const char *host,
int32_t port,
nsIProxyInfo *proxy,
const nsACString &firstPartyDomain,
const NeckoOriginAttributes &originAttributes,
uint32_t flags,
PRFileDesc **result,
nsISupports **socksInfo)
@ -88,7 +90,7 @@ nsSOCKSSocketProvider::AddToSocket(int32_t family,
const char *host,
int32_t port,
nsIProxyInfo *proxy,
const nsACString &firstPartyDomain,
const NeckoOriginAttributes &originAttributes,
uint32_t flags,
PRFileDesc *sock,
nsISupports **socksInfo)

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

@ -6,6 +6,8 @@
#include "nspr.h"
using mozilla::NeckoOriginAttributes;
NS_IMPL_ISUPPORTS(nsUDPSocketProvider, nsISocketProvider)
nsUDPSocketProvider::~nsUDPSocketProvider()
@ -17,7 +19,7 @@ nsUDPSocketProvider::NewSocket(int32_t aFamily,
const char *aHost,
int32_t aPort,
nsIProxyInfo *aProxy,
const nsACString &firstPartyDomain,
const NeckoOriginAttributes &originAttributes,
uint32_t aFlags,
PRFileDesc * *aFileDesc,
nsISupports **aSecurityInfo)
@ -37,7 +39,7 @@ nsUDPSocketProvider::AddToSocket(int32_t aFamily,
const char *aHost,
int32_t aPort,
nsIProxyInfo *aProxy,
const nsACString &firstPartyDomain,
const NeckoOriginAttributes &originAttributes,
uint32_t aFlags,
struct PRFileDesc * aFileDesc,
nsISupports **aSecurityInfo)

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

@ -9,9 +9,15 @@ import sys
from mozbuild.preprocessor import Preprocessor
def generate(output, *args):
pp = Preprocessor()
pp.out = output
pp.handleCommandLine(list(args), True)
return set(pp.includes)
def main(args):
pp = Preprocessor()
pp.handleCommandLine(args, True)
pp = Preprocessor()
pp.handleCommandLine(args, True)
if __name__ == "__main__":

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

@ -591,18 +591,18 @@ class RecursiveMakeBackend(CommonBackend):
self._process_host_library(obj, backend_file)
self._process_linked_libraries(obj, backend_file)
elif isinstance(obj, FinalTargetFiles):
self._process_final_target_files(obj, obj.files, backend_file)
elif isinstance(obj, FinalTargetPreprocessedFiles):
self._process_final_target_pp_files(obj, obj.files, backend_file, 'DIST_FILES')
elif isinstance(obj, ObjdirFiles):
self._process_objdir_files(obj, obj.files, backend_file)
elif isinstance(obj, ObjdirPreprocessedFiles):
self._process_final_target_pp_files(obj, obj.files, backend_file, 'OBJDIR_PP_FILES')
elif isinstance(obj, FinalTargetFiles):
self._process_final_target_files(obj, obj.files, backend_file)
elif isinstance(obj, FinalTargetPreprocessedFiles):
self._process_final_target_pp_files(obj, obj.files, backend_file, 'DIST_FILES')
elif isinstance(obj, AndroidResDirs):
# Order matters.
for p in obj.paths:
@ -1045,8 +1045,9 @@ class RecursiveMakeBackend(CommonBackend):
def _process_test_manifest(self, obj, backend_file):
# Much of the logic in this function could be moved to CommonBackend.
self.backend_input_files.add(mozpath.join(obj.topsrcdir,
obj.manifest_relpath))
for source in obj.source_relpaths:
self.backend_input_files.add(mozpath.join(obj.topsrcdir,
source))
# Don't allow files to be defined multiple times unless it is allowed.
# We currently allow duplicates for non-test files or test files if

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

@ -963,13 +963,15 @@ VARIABLES = {
'GENERATED_FILES': (StrictOrderingOnAppendListWithFlagsFactory({
'script': unicode,
'inputs': list }), list,
'inputs': list,
'flags': list, }), list,
"""Generic generated files.
This variable contains a list of files for the build system to
generate at export time. The generation method may be declared
with optional ``script`` and ``inputs`` flags on individual entries.
If the optional ``script`` flag is not present on an entry, it
with optional ``script``, ``inputs`` and ``flags`` attributes on
individual entries.
If the optional ``script`` attribute is not present on an entry, it
is assumed that rules for generating the file are present in
the associated Makefile.in.
@ -1003,6 +1005,9 @@ VARIABLES = {
The chosen script entry point may optionally return a set of strings,
indicating extra files the output depends on.
When the ``flags`` attribute is present, the given list of flags is
passed as extra arguments following the inputs.
"""),
'DEFINES': (InitializedDefines, dict,

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

@ -641,13 +641,17 @@ class TestManifest(ContextDerived):
# The relative path of the parsed manifest within the objdir.
'manifest_obj_relpath',
# The relative paths to all source files for this manifest.
'source_relpaths',
# If this manifest is a duplicate of another one, this is the
# manifestparser.TestManifest of the other one.
'dupe_manifest',
)
def __init__(self, context, path, manifest, flavor=None,
install_prefix=None, relpath=None, dupe_manifest=False):
install_prefix=None, relpath=None, sources=(),
dupe_manifest=False):
ContextDerived.__init__(self, context)
assert flavor in all_test_flavors()
@ -659,6 +663,7 @@ class TestManifest(ContextDerived):
self.install_prefix = install_prefix
self.manifest_relpath = relpath
self.manifest_obj_relpath = relpath
self.source_relpaths = sources
self.dupe_manifest = dupe_manifest
self.installs = {}
self.pattern_installs = []
@ -886,31 +891,19 @@ class FinalTargetPreprocessedFiles(ContextDerived):
self.files = files
class ObjdirFiles(ContextDerived):
class ObjdirFiles(FinalTargetFiles):
"""Sandbox container object for OBJDIR_FILES, which is a
HierarchicalStringList.
"""
__slots__ = ('files')
def __init__(self, sandbox, files):
ContextDerived.__init__(self, sandbox)
self.files = files
@property
def install_target(self):
return ''
class ObjdirPreprocessedFiles(ContextDerived):
class ObjdirPreprocessedFiles(FinalTargetPreprocessedFiles):
"""Sandbox container object for OBJDIR_PP_FILES, which is a
HierarchicalStringList.
"""
__slots__ = ('files')
def __init__(self, sandbox, files):
ContextDerived.__init__(self, sandbox)
self.files = files
@property
def install_target(self):
return ''

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

@ -128,6 +128,7 @@ ALLOWED_XPCOM_GLUE = {
('TestMailCookie', 'mailnews/base/test'),
('calbasecomps', 'calendar/base/backend/libical/build'),
('purplexpcom', 'extensions/purple/purplexpcom/src'),
('ipdlunittest', 'ipc/ipdl/test/cxx/app'),
}
@ -1228,7 +1229,8 @@ class TreeMetadataEmitter(LoggingMixin):
else:
script = None
method = None
yield GeneratedFile(context, script, method, outputs, inputs)
yield GeneratedFile(context, script, method, outputs, inputs,
flags.flags)
def _process_test_manifests(self, context):
for prefix, info in TEST_MANIFESTS.items():
@ -1253,6 +1255,8 @@ class TreeMetadataEmitter(LoggingMixin):
manifest_dir = mozpath.dirname(path)
manifest_reldir = mozpath.dirname(mozpath.relpath(path,
context.config.topsrcdir))
manifest_sources = [mozpath.relpath(pth, context.config.topsrcdir)
for pth in mpmanifest.source_files]
install_prefix = mozpath.join(install_root, install_subdir)
try:
@ -1264,6 +1268,7 @@ class TreeMetadataEmitter(LoggingMixin):
obj = TestManifest(context, path, mpmanifest, flavor=flavor,
install_prefix=install_prefix,
relpath=mozpath.join(manifest_reldir, mozpath.basename(path)),
sources=manifest_sources,
dupe_manifest='dupe-manifest' in defaults)
filtered = mpmanifest.tests

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

@ -328,7 +328,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
/*optional*/ const Flags flags,
/*optional*/ const SECItem* stapledOCSPResponseSECItem,
/*optional*/ const SECItem* sctsFromTLSSECItem,
/*optional*/ const char* firstPartyDomain,
/*optional*/ const NeckoOriginAttributes& originAttributes,
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
/*optional out*/ KeySizeStatus* keySizeStatus,
@ -422,7 +422,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed,
NetscapeStepUpPolicy::NeverMatch,
firstPartyDomain,
originAttributes,
builtChain, nullptr, nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity,
@ -496,7 +496,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS,
ValidityCheckingMode::CheckForEV,
sha1ModeConfigurations[i], mNetscapeStepUpPolicy,
firstPartyDomain, builtChain, pinningTelemetryInfo,
originAttributes, builtChain, pinningTelemetryInfo,
hostname);
rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
KeyUsage::digitalSignature,// (EC)DHE
@ -584,7 +584,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
ValidityCheckingMode::CheckingOff,
sha1ModeConfigurations[j],
mNetscapeStepUpPolicy,
firstPartyDomain, builtChain,
originAttributes, builtChain,
pinningTelemetryInfo, hostname);
rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
KeyUsage::digitalSignature,//(EC)DHE
@ -649,7 +649,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed, mNetscapeStepUpPolicy,
firstPartyDomain, builtChain, nullptr,
originAttributes, builtChain, nullptr,
nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign,
@ -666,7 +666,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed,
NetscapeStepUpPolicy::NeverMatch,
firstPartyDomain, builtChain, nullptr,
originAttributes, builtChain, nullptr,
nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity,
@ -694,7 +694,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed,
NetscapeStepUpPolicy::NeverMatch,
firstPartyDomain, builtChain, nullptr,
originAttributes, builtChain, nullptr,
nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity,
@ -719,7 +719,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed,
NetscapeStepUpPolicy::NeverMatch,
firstPartyDomain, builtChain, nullptr,
originAttributes, builtChain, nullptr,
nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity,
@ -753,7 +753,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed,
NetscapeStepUpPolicy::NeverMatch,
firstPartyDomain, builtChain, nullptr,
originAttributes, builtChain, nullptr,
nullptr);
rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA,
keyUsage, eku, CertPolicyId::anyPolicy,
@ -766,7 +766,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed,
NetscapeStepUpPolicy::NeverMatch,
firstPartyDomain, builtChain, nullptr,
originAttributes, builtChain, nullptr,
nullptr);
rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA,
keyUsage, eku, CertPolicyId::anyPolicy,
@ -781,7 +781,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed,
NetscapeStepUpPolicy::NeverMatch,
firstPartyDomain, builtChain,
originAttributes, builtChain,
nullptr, nullptr);
rv = BuildCertChain(objectSigningTrust, certDER, time,
endEntityOrCA, keyUsage, eku,
@ -813,7 +813,7 @@ CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
/*out*/ UniqueCERTCertList& builtChain,
/*optional*/ bool saveIntermediatesInPermanentDatabase,
/*optional*/ Flags flags,
/*optional*/ const char* firstPartyDomain,
/*optional*/ const NeckoOriginAttributes& originAttributes,
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
/*optional out*/ KeySizeStatus* keySizeStatus,
@ -838,7 +838,7 @@ CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
// if VerifyCert succeeded.
Result rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time,
pinarg, hostname, builtChain, flags,
stapledOCSPResponse, sctsFromTLS, firstPartyDomain,
stapledOCSPResponse, sctsFromTLS, originAttributes,
evOidPolicy, ocspStaplingStatus, keySizeStatus,
sha1ModeResult, pinningTelemetryInfo, ctInfo);
if (rv != Success) {

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

@ -15,6 +15,24 @@
#include "mozilla/UniquePtr.h"
#include "pkix/pkixtypes.h"
#if defined(_MSC_VER)
#pragma warning(push)
// Silence "RootingAPI.h(718): warning C4324: 'js::DispatchWrapper<T>':
// structure was padded due to alignment specifier with [ T=void * ]"
#pragma warning(disable:4324)
// Silence "Value.h(448): warning C4365: 'return': conversion from 'const
// int32_t' to 'JS::Value::PayloadType', signed/unsigned mismatch"
#pragma warning(disable:4365)
// Silence "warning C5031: #pragma warning(pop): likely mismatch, popping
// warning state pushed in different file
#pragma warning(disable:5031)
#endif /* defined(_MSC_VER) */
#include "mozilla/BasePrincipal.h"
#if defined(_MSC_VER)
#pragma warning(pop) /* popping the pragma in Vector.h */
#pragma warning(pop) /* popping the pragma in this file */
#endif /* defined(_MSC_VER) */
namespace mozilla { namespace ct {
// Including MultiLogCTVerifier.h would bring along all of its dependent
@ -109,7 +127,8 @@ public:
Flags flags = 0,
/*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
/*optional in*/ const SECItem* sctsFromTLS = nullptr,
/*optional in*/ const char* firstPartyDomain = nullptr,
/*optional in*/ const NeckoOriginAttributes& originAttributes =
NeckoOriginAttributes(),
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
@ -127,7 +146,8 @@ public:
/*out*/ UniqueCERTCertList& builtChain,
/*optional*/ bool saveIntermediatesInPermanentDatabase = false,
/*optional*/ Flags flags = 0,
/*optional*/ const char* firstPartyDomain = nullptr,
/*optional*/ const NeckoOriginAttributes& originAttributes =
NeckoOriginAttributes(),
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,

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