From 733e1b0c8fd3b4496fb9c6b33446a814927ee424 Mon Sep 17 00:00:00 2001 From: Frederik Braun Date: Thu, 21 Oct 2021 07:09:51 +0000 Subject: [PATCH 01/35] Bug 1736786 - use option.textContent in devtools css filter widget r=jdescottes Differential Revision: https://phabricator.services.mozilla.com/D128990 --- devtools/client/shared/widgets/FilterWidget.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/shared/widgets/FilterWidget.js index 635a39510bcf4..bb24be3865cba 100644 --- a/devtools/client/shared/widgets/FilterWidget.js +++ b/devtools/client/shared/widgets/FilterWidget.js @@ -257,8 +257,7 @@ CSSFilterEditorWidget.prototype = { const select = this.filterSelect; filterList.forEach(filter => { const option = this.doc.createElementNS(XHTML_NS, "option"); - // eslint-disable-next-line no-unsanitized/property - option.innerHTML = option.value = filter.name; + option.textContent = option.value = filter.name; select.appendChild(option); }); }, From fea049f963b78b67a2cee2af017b5ca9acb52efa Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 21 Oct 2021 08:54:49 +0000 Subject: [PATCH 02/35] Bug 1711477 - Update etagere to 0.2.6. r=gfx-reviewers,jrmuizel The newer version contains a few new APIs that will be needed for texture cache compaction: - An iterator of allocated items. - A way to associate AllocIds with a stable index. Differential Revision: https://phabricator.services.mozilla.com/D128251 --- Cargo.lock | 4 +- gfx/wr/Cargo.lock | 6 +- gfx/wr/webrender/Cargo.toml | 2 +- third_party/rust/etagere/.cargo-checksum.json | 2 +- third_party/rust/etagere/Cargo.toml | 11 +- third_party/rust/etagere/README.md | 6 +- third_party/rust/etagere/src/allocator.rs | 108 +++++++++++++++++- third_party/rust/etagere/src/lib.rs | 21 +++- 8 files changed, 139 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 77b9bc1ea2fe0..14cbb30b3ac0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1360,9 +1360,9 @@ dependencies = [ [[package]] name = "etagere" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520d7de540904fd09b11c03a47d50a7ce4ff37d1aa763f454fa60d9088ef8356" +checksum = "5eb66dc3d6bb6b2ab4a12454db6988079311d6443e627bc7e6065f907f556272" dependencies = [ "euclid", "serde", diff --git a/gfx/wr/Cargo.lock b/gfx/wr/Cargo.lock index b3a3b9b7d65fe..76401cbc9e342 100644 --- a/gfx/wr/Cargo.lock +++ b/gfx/wr/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "adler32" version = "1.0.4" @@ -508,9 +510,9 @@ dependencies = [ [[package]] name = "etagere" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520d7de540904fd09b11c03a47d50a7ce4ff37d1aa763f454fa60d9088ef8356" +checksum = "5eb66dc3d6bb6b2ab4a12454db6988079311d6443e627bc7e6065f907f556272" dependencies = [ "euclid", "serde", diff --git a/gfx/wr/webrender/Cargo.toml b/gfx/wr/webrender/Cargo.toml index 1c8f0bde58500..24f73b5d111da 100644 --- a/gfx/wr/webrender/Cargo.toml +++ b/gfx/wr/webrender/Cargo.toml @@ -50,7 +50,7 @@ malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = " svg_fmt = "0.4" tracy-rs = "0.1.2" derive_more = "0.99" -etagere = "0.2.4" +etagere = "0.2.6" swgl = { path = "../swgl", optional = true } [dev-dependencies] diff --git a/third_party/rust/etagere/.cargo-checksum.json b/third_party/rust/etagere/.cargo-checksum.json index b3fb8204c3108..e71286c786c0b 100644 --- a/third_party/rust/etagere/.cargo-checksum.json +++ b/third_party/rust/etagere/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"d35f36bc8f1841ebfdfefdf89472a9778c79d52e4fd72babc7fc63b83b719fcc","LICENSE":"739e55c73735c9733d8be0e3ab1c3bdb2240df594602c18eefbf8c851a83a734","README.md":"c33b45963747bb52370df2d4b44ef8ed6073e1874c1394b09ccdf33cb7c8c606","src/allocator.rs":"1f2d80519804b553c011a9eec757e4a7ddb2475b55a1dd829a7cabb15ca1a22b","src/bucketed.rs":"caad4039803df2eaf0b326c900b608f5707dda9066a72c87f95c98de87f96e0c","src/lib.rs":"f0ed9bd3c748de24cb9bdeeaf5ca6665e04882787db579fa18aa1e57481a51fb"},"package":"520d7de540904fd09b11c03a47d50a7ce4ff37d1aa763f454fa60d9088ef8356"} \ No newline at end of file +{"files":{"Cargo.toml":"73cc4cf224804322fbdaba24e3ac53c08ae8b3a39ac272d88f6367939efaeed0","LICENSE":"739e55c73735c9733d8be0e3ab1c3bdb2240df594602c18eefbf8c851a83a734","README.md":"e2b6efd7d6421180a9aa8f06cbfe6092dda4184408857865be18c429d408e12d","src/allocator.rs":"4e8deb24af712bd55558c213fef772f036687c2f03f49de78e9152dcd9a4dda9","src/bucketed.rs":"caad4039803df2eaf0b326c900b608f5707dda9066a72c87f95c98de87f96e0c","src/lib.rs":"ca8c534dcbccfc8c694b40622dbcecb45062bf79b36e1f5e000b680851059cb1"},"package":"5eb66dc3d6bb6b2ab4a12454db6988079311d6443e627bc7e6065f907f556272"} \ No newline at end of file diff --git a/third_party/rust/etagere/Cargo.toml b/third_party/rust/etagere/Cargo.toml index f5140ec2c6184..44b14f2343347 100644 --- a/third_party/rust/etagere/Cargo.toml +++ b/third_party/rust/etagere/Cargo.toml @@ -3,17 +3,16 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "etagere" -version = "0.2.4" +version = "0.2.6" authors = ["Nicolas Silva "] exclude = [".backup*"] description = "Dynamic 2D texture atlas allocation using the shelf packing algorithm." diff --git a/third_party/rust/etagere/README.md b/third_party/rust/etagere/README.md index 20677de29c8ce..da4cac4e146f7 100644 --- a/third_party/rust/etagere/README.md +++ b/third_party/rust/etagere/README.md @@ -2,7 +2,7 @@

- crates.io + crates.io documentation @@ -44,8 +44,8 @@ atlas.deallocate(b.id); Licensed under either of - * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. diff --git a/third_party/rust/etagere/src/allocator.rs b/third_party/rust/etagere/src/allocator.rs index 7b7187e1616b3..0863b08be79de 100644 --- a/third_party/rust/etagere/src/allocator.rs +++ b/third_party/rust/etagere/src/allocator.rs @@ -54,6 +54,7 @@ struct Item { next: ItemIndex, shelf: ShelfIndex, allocated: bool, + generation: u16, } // Note: if allocating is slow we can use the guillotiere trick of storing multiple lists of free @@ -156,6 +157,7 @@ impl AtlasAllocator { next: ItemIndex::NONE, shelf: current, allocated: false, + generation: 1, }); prev = current; @@ -264,6 +266,7 @@ impl AtlasAllocator { next: ItemIndex::NONE, shelf: new_shelf_idx, allocated: false, + generation: 1, }); self.shelves[new_shelf_idx.index()].first_item = new_item_idx; @@ -290,6 +293,7 @@ impl AtlasAllocator { next: item.next, shelf: item.shelf, allocated: false, + generation: 1, }); self.items[selected_item.index()].width = width; @@ -303,6 +307,7 @@ impl AtlasAllocator { } self.items[selected_item.index()].allocated = true; + let generation = self.items[selected_item.index()].generation; let x0 = item.x; let y0 = shelf.y; @@ -322,18 +327,19 @@ impl AtlasAllocator { self.allocated_space += rectangle.area(); Some(Allocation { - id: AllocId(selected_item.0 as u32), + id: AllocId::new(selected_item.0, generation), rectangle, }) } /// Deallocate a rectangle in the atlas. pub fn deallocate(&mut self, id: AllocId) { - let item_idx = ItemIndex(id.0 as u16); + let item_idx = ItemIndex(id.index()); //let item = self.items[item_idx.index()].clone(); - let Item { mut prev, mut next, mut width, allocated, shelf, .. } = self.items[item_idx.index()]; + let Item { mut prev, mut next, mut width, allocated, shelf, generation, .. } = self.items[item_idx.index()]; assert!(allocated); + assert_eq!(generation, id.generation(), "Invalid AllocId"); self.items[item_idx.index()].allocated = false; self.allocated_space -= width as i32 * self.shelves[shelf.index()].height as i32; @@ -450,6 +456,13 @@ impl AtlasAllocator { self.size.area() - self.allocated_space } + pub fn iter(&self) -> Iter { + Iter { + atlas: self, + idx: 0, + } + } + fn remove_item(&mut self, idx: ItemIndex) { self.items[idx.index()].next = self.free_items; self.free_items = idx; @@ -463,9 +476,10 @@ impl AtlasAllocator { self.free_shelves = idx; } - fn add_item(&mut self, item: Item) -> ItemIndex { + fn add_item(&mut self, mut item: Item) -> ItemIndex { if self.free_items.is_some() { let idx = self.free_items; + item.generation = self.items[idx.index()].generation.wrapping_add(1); self.free_items = self.items[idx.index()].next; self.items[idx.index()] = item; @@ -545,6 +559,25 @@ impl AtlasAllocator { } } + /// Turn a valid AllocId into an index that can be used as a key for external storage. + /// + /// The allocator internally stores all items in a single vector. In addition allocations + /// stay at the same index in the vector until they are deallocated. As a result the index + /// of an item can be used as a key for external storage using vectors. Note that: + /// - The provided ID must correspond to an item that is currently allocated in the atlas. + /// - After an item is deallocated, its index may be reused by a future allocation, so + /// the returned index should only be considered valid during the lifetime of the its + /// associated item. + /// - indices are expected to be "reasonable" with respect to the number of allocated items, + /// in other words it is never larger than the maximum number of allocated items in the + /// atlas (making it a good fit for indexing within a sparsely populated vector). + pub fn get_index(&self, id: AllocId) -> u32 { + let index = id.index(); + debug_assert_eq!(self.items[index as usize].generation, id.generation()); + + index as u32 + } + /// Dump a visual representation of the atlas in SVG format. pub fn dump_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> { use svg_fmt::*; @@ -661,6 +694,63 @@ fn shelf_height(mut size: i32) -> i32 { size } +/// Iterator over the allocations of an atlas. +pub struct Iter<'l> { + atlas: &'l AtlasAllocator, + idx: usize, +} + +impl<'l> Iterator for Iter<'l> { + type Item = Allocation; + + fn next(&mut self) -> Option { + if self.idx >= self.atlas.items.len() { + return None; + } + + while !self.atlas.items[self.idx].allocated { + self.idx += 1; + if self.idx >= self.atlas.items.len() { + return None; + } + } + + let item = &self.atlas.items[self.idx]; + let shelf = &self.atlas.shelves[item.shelf.index()]; + + let mut alloc = Allocation { + rectangle: Rectangle { + min: point2( + item.x as i32, + shelf.y as i32, + ), + max: point2( + (item.x + item.width) as i32, + (shelf.y + shelf.height) as i32, + ), + }, + id: AllocId::new(self.idx as u16, item.generation), + }; + + if self.atlas.flip_xy { + std::mem::swap(&mut alloc.rectangle.min.x, &mut alloc.rectangle.min.y); + std::mem::swap(&mut alloc.rectangle.max.x, &mut alloc.rectangle.max.y); + } + + self.idx += 1; + + Some(alloc) + } +} + +impl<'l> std::iter::IntoIterator for &'l AtlasAllocator { + type Item = Allocation; + type IntoIter = Iter<'l>; + fn into_iter(self) -> Iter<'l> { + self.iter() + } +} + #[test] fn test_simple() { let mut atlas = AtlasAllocator::with_options( @@ -716,6 +806,10 @@ fn test_options() { assert!(a1.id != a3.id); assert!(!atlas.is_empty()); + for id in &atlas { + assert!(id == a1 || id == a2 || id == a3); + } + assert_eq!(a1.rectangle.min.x % alignment.width, 0); assert_eq!(a1.rectangle.min.y % alignment.height, 0); assert_eq!(a2.rectangle.min.x % alignment.width, 0); @@ -762,10 +856,14 @@ fn vertical() { let c = atlas.allocate(size2(128, 128)).unwrap(); + for _ in &atlas {} + atlas.deallocate(a.id); atlas.deallocate(b.id); atlas.deallocate(c.id); + for _ in &atlas {} + assert!(atlas.is_empty()); assert_eq!(atlas.allocated_space(), 0); } @@ -858,6 +956,8 @@ fn clear() { atlas.allocate(size2(11, 12)).unwrap(); atlas.allocate(size2(29, 28)).unwrap(); atlas.allocate(size2(32, 32)).unwrap(); + + for _ in &atlas {} } } diff --git a/third_party/rust/etagere/src/lib.rs b/third_party/rust/etagere/src/lib.rs index 5a491fac4cecd..a8fd4e7ad4e57 100644 --- a/third_party/rust/etagere/src/lib.rs +++ b/third_party/rust/etagere/src/lib.rs @@ -64,8 +64,8 @@ //! //! Licensed under either of //! -//! * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -//! * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +//! * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) +//! * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) //! //! at your option. //! @@ -137,10 +137,27 @@ pub struct Allocation { pub struct AllocId(pub(crate) u32); impl AllocId { + #[inline] + pub(crate) fn new(index: u16, gen: u16) -> Self { + AllocId(index as u32 | ((gen as u32) << 16)) + } + + #[inline] + pub(crate) fn index(&self) -> u16 { + self.0 as u16 + } + + #[inline] + pub(crate) fn generation(&self) -> u16 { + (self.0 >> 16) as u16 + } + + #[inline] pub fn serialize(&self) -> u32 { self.0 } + #[inline] pub fn deserialize(bytes: u32) -> Self { AllocId(bytes) } From fce170afcb8f1ec66500419d7bb61bd476338c67 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 21 Oct 2021 08:54:49 +0000 Subject: [PATCH 03/35] Bug 1711477 - Add a way to get from atlas alloc ID to texture cache handle. r=gfx-reviewers,gw This will be used in a followup patch to defragment the texture cache. Differential Revision: https://phabricator.services.mozilla.com/D128252 --- gfx/wr/webrender/src/texture_cache.rs | 44 +++++++++++++----------- gfx/wr/webrender/src/texture_pack/mod.rs | 30 ++++++++++++++-- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/gfx/wr/webrender/src/texture_cache.rs b/gfx/wr/webrender/src/texture_cache.rs index 58cb1a3356665..51aff139aa7f1 100644 --- a/gfx/wr/webrender/src/texture_cache.rs +++ b/gfx/wr/webrender/src/texture_cache.rs @@ -49,7 +49,7 @@ pub const TEXTURE_REGION_DIMENSIONS: i32 = 512; /// Items in the texture cache can either be standalone textures, /// or a sub-rect inside the shared cache. -#[derive(Debug)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum EntryDetails { @@ -1349,36 +1349,28 @@ impl TextureCache { ), BudgetType::Standalone) } - /// Allocates a cache entry appropriate for the given parameters. - /// - /// This allocates from the shared cache unless the parameters do not meet - /// the shared cache requirements, in which case a standalone texture is - /// used. - fn allocate_cache_entry( + /// Allocates a cache entry for the given parameters, and updates the + /// provided handle to point to the new entry. + fn allocate( &mut self, params: &CacheAllocParams, - ) -> (CacheEntry, BudgetType) { + handle: &mut TextureCacheHandle, + eviction: Eviction, + ) { + debug_assert!(self.now.is_valid()); assert!(!params.descriptor.size.is_empty()); // If this image doesn't qualify to go in the shared (batching) cache, // allocate a standalone entry. - if self.is_allowed_in_shared_cache(params.filter, ¶ms.descriptor) { + let use_shared_cache = self.is_allowed_in_shared_cache(params.filter, ¶ms.descriptor); + let (new_cache_entry, budget_type) = if use_shared_cache { self.allocate_from_shared_cache(params) } else { self.allocate_standalone_entry(params) - } - } + }; - /// Allocates a cache entry for the given parameters, and updates the - /// provided handle to point to the new entry. - fn allocate( - &mut self, - params: &CacheAllocParams, - handle: &mut TextureCacheHandle, - eviction: Eviction, - ) { - debug_assert!(self.now.is_valid()); - let (new_cache_entry, budget_type) = self.allocate_cache_entry(params); + let details = new_cache_entry.details.clone(); + let texture_id = new_cache_entry.texture_id; // If the handle points to a valid cache entry, we want to replace the // cache entry with our newly updated location. We also need to ensure @@ -1417,6 +1409,16 @@ impl TextureCache { old_entry.evict(); self.free(&old_entry); } + + if let EntryDetails::Cache { alloc_id, .. } = details { + let allocator_list = self.shared_textures.select( + params.descriptor.format, + params.filter, + params.shader, + ).0; + + allocator_list.set_handle(texture_id, alloc_id, handle); + } } pub fn shared_alpha_expected_format(&self) -> ImageFormat { diff --git a/gfx/wr/webrender/src/texture_pack/mod.rs b/gfx/wr/webrender/src/texture_pack/mod.rs index 8985271b66102..7e2a4c41387e3 100644 --- a/gfx/wr/webrender/src/texture_pack/mod.rs +++ b/gfx/wr/webrender/src/texture_pack/mod.rs @@ -3,7 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ mod guillotine; - +use crate::texture_cache::TextureCacheHandle; +use crate::internal_types::FastHashMap; pub use guillotine::*; /* This Source Code Form is subject to the terms of the Mozilla Public @@ -54,6 +55,10 @@ pub trait AtlasAllocatorList { texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId, ) -> (CacheTextureId, AllocId, DeviceIntRect); + fn set_handle(&mut self, texture_id: CacheTextureId, alloc_id: AllocId, handle: &TextureCacheHandle); + + fn remove_handle(&mut self, texture_id: CacheTextureId, alloc_id: AllocId); + /// Deallocate a rectangle and return its size. fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId); @@ -65,6 +70,7 @@ pub trait AtlasAllocatorList { #[cfg_attr(feature = "replay", derive(Deserialize))] struct TextureUnit { allocator: Allocator, + handles: FastHashMap, texture_id: CacheTextureId, } @@ -109,6 +115,7 @@ impl AllocatorList AllocatorList { self.allocate(requested_size, texture_alloc_cb) } + fn set_handle(&mut self, texture_id: CacheTextureId, alloc_id: AllocId, handle: &TextureCacheHandle) { + let unit = self.units + .iter_mut() + .find(|unit| unit.texture_id == texture_id) + .expect("Unable to find the associated texture array unit"); + unit.handles.insert(alloc_id, handle.clone()); + } + + fn remove_handle(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) { + let unit = self.units + .iter_mut() + .find(|unit| unit.texture_id == texture_id) + .expect("Unable to find the associated texture array unit"); + unit.handles.remove(&alloc_id); + } + fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) { self.deallocate(texture_id, alloc_id); } @@ -301,7 +325,9 @@ fn bug_1680769() { // Make some allocations, forcing the the creation of multiple textures. for _ in 0..50 { - allocations.push(allocators.allocate(size2(256, 256), alloc_cb)); + let alloc = allocators.allocate(size2(256, 256), alloc_cb); + allocators.set_handle(alloc.0, alloc.1, &TextureCacheHandle::Empty); + allocations.push(alloc); } // Deallocate everything. From 5cb683cc8f7a6e81e59b2040950bc5c59702dc92 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 21 Oct 2021 08:54:50 +0000 Subject: [PATCH 04/35] Bug 1711477 - Add support for copying from a cache texture to another. r=gfx-reviewers,kvark The initial implementation uses the composite shader just like the upload code, but it's a bit messy. I'll add a simpler more specialized shader for that in a followup. Differential Revision: https://phabricator.services.mozilla.com/D128253 --- gfx/wr/webrender/src/internal_types.rs | 30 +++++++++ gfx/wr/webrender/src/renderer/mod.rs | 88 ++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/gfx/wr/webrender/src/internal_types.rs b/gfx/wr/webrender/src/internal_types.rs index 34f2d14cde9ea..1678601060e0a 100644 --- a/gfx/wr/webrender/src/internal_types.rs +++ b/gfx/wr/webrender/src/internal_types.rs @@ -497,6 +497,13 @@ pub struct TextureCacheUpdate { pub source: TextureUpdateSource, } +/// Command to update the contents of the texture cache. +#[derive(Debug)] +pub struct TextureCacheCopy { + pub src_rect: DeviceIntRect, + pub dst_rect: DeviceIntRect, +} + /// Atomic set of commands to manipulate the texture cache, generated on the /// RenderBackend thread and executed on the Renderer thread. /// @@ -511,6 +518,9 @@ pub struct TextureUpdateList { pub allocations: Vec, /// Commands to update the contents of the textures. Processed second. pub updates: FastHashMap>, + /// Commands to move items within the cache, these are applied before everything + /// else in the update list. + pub copies: FastHashMap<(CacheTextureId, CacheTextureId), Vec>, } impl TextureUpdateList { @@ -520,6 +530,7 @@ impl TextureUpdateList { clears_shared_cache: false, allocations: Vec::new(), updates: FastHashMap::default(), + copies: FastHashMap::default(), } } @@ -623,6 +634,25 @@ impl TextureUpdateList { }; } + /// Push a copy operation from a texture to another. + /// + /// The source and destination rectangles must have the same size. + /// The copies are applied before every other operations in the + /// texture update list. + pub fn push_copy( + &mut self, + src_id: CacheTextureId, src_rect: &DeviceIntRect, + dst_id: CacheTextureId, dst_rect: &DeviceIntRect, + ) { + debug_assert_eq!(src_rect.size(), dst_rect.size()); + self.copies.entry((src_id, dst_id)) + .or_insert_with(Vec::new) + .push(TextureCacheCopy { + src_rect: *src_rect, + dst_rect: *dst_rect, + }); + } + fn debug_assert_coalesced(&self, id: CacheTextureId) { debug_assert!( self.allocations.iter().filter(|x| x.id == id).count() <= 1, diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs index d626b1b9c7cf5..a8482876c16d6 100644 --- a/gfx/wr/webrender/src/renderer/mod.rs +++ b/gfx/wr/webrender/src/renderer/mod.rs @@ -2249,6 +2249,93 @@ impl Renderer { let mut delete_cache_texture_time = 0; for update_list in pending_texture_updates.drain(..) { + // Handle copies from one texture to another. + for ((src_tex, dst_tex), copies) in &update_list.copies { + let mut copy_instances = Vec::new(); + + let src_texture = &self.texture_resolver.texture_cache_map[&src_tex].texture; + let dest_texture = &self.texture_resolver.texture_cache_map[&dst_tex].texture; + + self.device.bind_texture( + TextureSampler::Color0, + src_texture, + Swizzle::default(), + ); + + let target_size = dest_texture.get_dimensions(); + + let draw_target = DrawTarget::from_texture( + dest_texture, + false, + ); + self.device.bind_draw_target(draw_target); + + let projection = Transform3D::ortho( + 0.0, + target_size.width as f32, + 0.0, + target_size.height as f32, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), + ); + + self.shaders + .borrow_mut() + .get_composite_shader( + CompositeSurfaceFormat::Rgba, + ImageBufferKind::Texture2D, + CompositeFeatures::empty(), + ).bind( + &mut self.device, + &projection, + None, + &mut self.renderer_errors, + &mut self.profile, + ); + + for copy in copies { + let uv_rect = TexelRect::new( + copy.src_rect.min.x as f32, + copy.src_rect.min.y as f32, + copy.src_rect.max.x as f32, + copy.src_rect.max.y as f32, + ); + + copy_instances.push(CompositeInstance::new_rgb( + copy.dst_rect.to_f32().cast_unit(), + copy.dst_rect.to_f32(), + PremultipliedColorF::WHITE, + ZBufferId(0), + uv_rect, + CompositorTransform::identity(), + )); + } + + let mut dummy_stats = RendererStats { + total_draw_calls: 0, + alpha_target_count: 0, + color_target_count: 0, + texture_upload_mb: 0.0, + resource_upload_time: 0.0, + gpu_cache_upload_time: 0.0, + gecko_display_list_time: 0.0, + wr_display_list_time: 0.0, + scene_build_time: 0.0, + frame_build_time: 0.0, + full_display_list: false, + full_paint: false, + }; + + self.draw_instanced_batch( + ©_instances, + VertexArrayKind::Composite, + // We bind the staging texture manually because it isn't known + // to the texture resolver. + &BatchTextures::empty(), + &mut dummy_stats, + ); + } + // Find any textures that will need to be deleted in this group of allocations. let mut pending_deletes = Vec::new(); for allocation in &update_list.allocations { @@ -2294,6 +2381,7 @@ impl Renderer { TextureCacheAllocationKind::Free => {} } } + // Now that we've saved as many deletions for reuse as we can, actually delete whatever is left. if !pending_deletes.is_empty() { let delete_texture_start = precise_time_ns(); From 3410a14baed42605c2407d92af980b91c3d14566 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 21 Oct 2021 08:54:50 +0000 Subject: [PATCH 05/35] Bug 1711477 - Texture cache compaction r=gfx-reviewers,kvark Differential Revision: https://phabricator.services.mozilla.com/D128254 --- gfx/wr/webrender/src/frame_builder.rs | 2 +- gfx/wr/webrender/src/resource_cache.rs | 4 +- gfx/wr/webrender/src/texture_cache.rs | 83 ++++++++++++++++++++-- gfx/wr/webrender/src/texture_pack/mod.rs | 89 +++++++++++++++++++++++- 4 files changed, 169 insertions(+), 9 deletions(-) diff --git a/gfx/wr/webrender/src/frame_builder.rs b/gfx/wr/webrender/src/frame_builder.rs index 151b9596112d3..eddb8a2f8ef70 100644 --- a/gfx/wr/webrender/src/frame_builder.rs +++ b/gfx/wr/webrender/src/frame_builder.rs @@ -552,8 +552,8 @@ impl FrameBuilder { profile.set(profiler::PRIMITIVES, scene.prim_store.prim_count()); profile.set(profiler::PICTURE_CACHE_SLICES, scene.tile_cache_config.picture_cache_slice_count); scratch.begin_frame(); - resource_cache.begin_frame(stamp, profile); gpu_cache.begin_frame(stamp); + resource_cache.begin_frame(stamp, gpu_cache, profile); self.globals.update(gpu_cache); diff --git a/gfx/wr/webrender/src/resource_cache.rs b/gfx/wr/webrender/src/resource_cache.rs index 4c906bf2bdfc9..8062e41bdece9 100644 --- a/gfx/wr/webrender/src/resource_cache.rs +++ b/gfx/wr/webrender/src/resource_cache.rs @@ -1229,7 +1229,7 @@ impl ResourceCache { }) } - pub fn begin_frame(&mut self, stamp: FrameStamp, profile: &mut TransactionProfile) { + pub fn begin_frame(&mut self, stamp: FrameStamp, gpu_cache: &mut GpuCache, profile: &mut TransactionProfile) { profile_scope!("begin_frame"); debug_assert_eq!(self.state, State::Idle); self.state = State::AddResources; @@ -1247,6 +1247,8 @@ impl ResourceCache { // pop the old frame and push a new one self.deleted_blob_keys.pop_front(); self.deleted_blob_keys.push_back(Vec::new()); + + self.texture_cache.run_compaction(gpu_cache); } pub fn block_until_all_resources_added( diff --git a/gfx/wr/webrender/src/texture_cache.rs b/gfx/wr/webrender/src/texture_cache.rs index 51aff139aa7f1..f8f78b82f73e2 100644 --- a/gfx/wr/webrender/src/texture_cache.rs +++ b/gfx/wr/webrender/src/texture_cache.rs @@ -20,11 +20,7 @@ use crate::lru_cache::LRUCache; use crate::profiler::{self, TransactionProfile}; use crate::resource_cache::{CacheItem, CachedImageData}; use crate::texture_pack::{ - AllocatorList, - AllocId, - AtlasAllocatorList, - ShelfAllocator, - ShelfAllocatorOptions, + AllocatorList, AllocId, AtlasAllocatorList, ShelfAllocator, ShelfAllocatorOptions, }; use std::cell::Cell; use std::mem; @@ -315,6 +311,7 @@ struct SharedTextures { color8_linear: AllocatorList, color8_glyphs: AllocatorList, bytes_per_texture_of_type: [i32 ; BudgetType::COUNT], + next_compaction_idx: usize, } impl SharedTextures { @@ -436,6 +433,7 @@ impl SharedTextures { color8_glyphs, color8_nearest, bytes_per_texture_of_type, + next_compaction_idx: 0, } } @@ -746,6 +744,79 @@ impl TextureCache { self.now = FrameStamp::INVALID; } + pub fn run_compaction(&mut self, gpu_cache: &mut GpuCache) { + // Use the same order as BudgetType::VALUES so that we can index self.bytes_allocated + // with the same index. + let allocator_lists = [ + &mut self.shared_textures.color8_linear, + &mut self.shared_textures.color8_nearest, + &mut self.shared_textures.color8_glyphs, + &mut self.shared_textures.alpha8_linear, + &mut self.shared_textures.alpha8_glyphs, + &mut self.shared_textures.alpha16_linear, + ]; + + // Pick a texture type on which to try to run the compaction logic this frame. + let idx = self.shared_textures.next_compaction_idx; + + // Number of moved pixels after which we stop attempting to move more items for this frame. + // The constant is up for adjustment, the main goal is to avoid causing frame spikes on + // low end GPUs. + let area_threshold = 512*512; + + let mut changes = Vec::new(); + allocator_lists[idx].try_compaction(area_threshold, &mut changes); + + if changes.is_empty() { + // Nothing to do, we'll try another texture type next frame. + self.shared_textures.next_compaction_idx = (self.shared_textures.next_compaction_idx + 1) % allocator_lists.len(); + } + + for change in changes { + let bpp = allocator_lists[idx].texture_parameters().formats.internal.bytes_per_pixel(); + + // While the area of the image does not change, the area it occupies in the texture + // atlas may (in other words the number of wasted pixels can change), so we have + // to keep track of that. + let old_bytes = (change.old_rect.area() * bpp) as usize; + let new_bytes = (change.new_rect.area() * bpp) as usize; + self.bytes_allocated[idx] -= old_bytes; + self.bytes_allocated[idx] += new_bytes; + + let entry = match change.handle { + TextureCacheHandle::Auto(handle) => self.lru_cache.get_opt_mut(&handle).unwrap(), + TextureCacheHandle::Manual(handle) => self.manual_entries.get_opt_mut(&handle).unwrap(), + TextureCacheHandle::Empty => { panic!("invalid handle"); } + }; + entry.texture_id = change.new_tex; + entry.details = EntryDetails::Cache { + origin: change.new_rect.min, + alloc_id: change.new_id, + allocated_size_in_bytes: new_bytes, + }; + + gpu_cache.invalidate(&entry.uv_rect_handle); + entry.uv_rect_handle = GpuCacheHandle::new(); + + let src_rect = DeviceIntRect::from_origin_and_size(change.old_rect.min, entry.size); + let dst_rect = DeviceIntRect::from_origin_and_size(change.new_rect.min, entry.size); + + self.pending_updates.push_copy(change.old_tex, &src_rect, change.new_tex, &dst_rect); + + if self.debug_flags.contains( + DebugFlags::TEXTURE_CACHE_DBG | + DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED) + { + self.pending_updates.push_debug_clear( + change.old_tex, + src_rect.min, + src_rect.width(), + src_rect.height(), + ); + } + } + } + // Request an item in the texture cache. All images that will // be used on a frame *must* have request() called on their // handle, to update the last used timestamp and ensure @@ -1535,8 +1606,8 @@ mod test_texture_cache { use api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, DirtyRect}; use api::units::*; use euclid::size2; - let mut gpu_cache = GpuCache::new_for_testing(); let mut texture_cache = TextureCache::new_for_testing(2048, ImageFormat::BGRA8); + let mut gpu_cache = GpuCache::new_for_testing(); let sizes: &[DeviceIntSize] = &[ size2(23, 27), diff --git a/gfx/wr/webrender/src/texture_pack/mod.rs b/gfx/wr/webrender/src/texture_pack/mod.rs index 7e2a4c41387e3..f89a82b0a1240 100644 --- a/gfx/wr/webrender/src/texture_pack/mod.rs +++ b/gfx/wr/webrender/src/texture_pack/mod.rs @@ -72,6 +72,10 @@ struct TextureUnit { allocator: Allocator, handles: FastHashMap, texture_id: CacheTextureId, + // The texture might become empty during a frame where we copy items out + // of it, in which case we want to postpone deleting the texture to the + // next frame. + delay_deallocation: bool, } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -117,6 +121,7 @@ impl AllocatorList AllocatorList(&mut self, texture_dealloc_cb: &'l mut dyn FnMut(CacheTextureId)) { self.units.retain(|unit| { - if unit.allocator.is_empty() { + if unit.allocator.is_empty() && !unit.delay_deallocation { texture_dealloc_cb(unit.texture_id); false } else{ + unit.delay_deallocation = false; true } }); @@ -306,6 +312,87 @@ impl AtlasAllocator for ShelfAllocator { } } +pub struct CompactionChange { + pub handle: TextureCacheHandle, + pub old_id: AllocId, + pub old_tex: CacheTextureId, + pub old_rect: DeviceIntRect, + pub new_id: AllocId, + pub new_tex: CacheTextureId, + pub new_rect: DeviceIntRect, +} + +impl

AllocatorList { + /// Attempt to move some allocations from a texture to another to reduce the number of textures. + pub fn try_compaction( + &mut self, + max_pixels: i32, + changes: &mut Vec, + ) { + // The goal here is to consolidate items in the first texture by moving them from the last. + + if self.units.len() < 2 { + // Nothing to do we are already "compact". + return; + } + + let last_unit = self.units.len() - 1; + let mut pixels = 0; + while let Some(alloc) = self.units[last_unit].allocator.iter().next() { + // For each allocation in the last texture, try to allocate it in the first one. + let new_alloc = match self.units[0].allocator.allocate(alloc.rectangle.size()) { + Some(new_alloc) => new_alloc, + None => { + // Stop when we fail to fit an item into the first texture. + // We could potentially fit another smaller item in there but we take it as + // an indication that the texture is more or less full, and we'll eventually + // manage to move the items later if they still exist as other items expire, + // which is what matters. + break; + } + }; + + // The item was successfully reallocated in the first texture, we can proceed + // with removing it from the last. + + // We keep track of the texture cache handle for each allocation, make sure + // the new allocation has the proper handle. + let alloc_id = AllocId(alloc.id.serialize()); + let new_alloc_id = AllocId(new_alloc.id.serialize()); + let handle = self.units[last_unit].handles.get(&alloc_id).unwrap().clone(); + self.units[0].handles.insert(new_alloc_id, handle.clone()); + + // Remove the allocation for the last texture. + self.units[last_unit].handles.remove(&alloc_id); + self.units[last_unit].allocator.deallocate(alloc.id); + + // Prevent the texture from being deleted on the same frame. + self.units[last_unit].delay_deallocation = true; + + // Record the change so that the texture cache can do additional bookkeeping. + changes.push(CompactionChange { + handle, + old_id: AllocId(alloc.id.serialize()), + old_tex: self.units[last_unit].texture_id, + old_rect: alloc.rectangle.cast_unit(), + new_id: AllocId(new_alloc.id.serialize()), + new_tex: self.units[0].texture_id, + new_rect: new_alloc.rectangle.cast_unit(), + }); + + // We are not in a hurry to move all allocations we can in one go, as long as we + // eventually have a chance to move them all within a reasonable amount of time. + // It's best to spread the load over multiple frames to avoid sudden spikes, so we + // stop after we have passed a certain threshold. + pixels += alloc.rectangle.area(); + if pixels > max_pixels { + break; + } + } + } + +} + #[test] fn bug_1680769() { let mut allocators: AllocatorList = AllocatorList::new( From 617736e4be86318f404ffb556168301e9711b246 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 21 Oct 2021 08:54:51 +0000 Subject: [PATCH 06/35] Bug 1711477 - Improve the texture cache eviction heuristics. r=gfx-reviewers,kvark. The previous heuristics would set a threshold in number of allocated bytes per texture type, continuously evict a fixed number of items above the threshold and stop evicting below the threshold. The new logic lowers the amount of allocated bytes below which we stop evicting, and make eviction above the threshold more progressive, only evicting very cold items if the the cache pressure is low and ramping up how aggressively items a are evicted along with the cache pressure. In addition, we maintain a minimum of cache pressure until there is a only a single texture atlas allocated for a given shared texture type. The above combined with the texture cache compaction code ensures that even after a difficult workload, the texture cache eventually settles back to a single texture atlas per type with reasonable fragmentation. Differential Revision: https://phabricator.services.mozilla.com/D128255 --- gfx/wr/webrender/src/texture_cache.rs | 163 ++++++++++++++++---------- 1 file changed, 103 insertions(+), 60 deletions(-) diff --git a/gfx/wr/webrender/src/texture_cache.rs b/gfx/wr/webrender/src/texture_cache.rs index f8f78b82f73e2..8650f12ecfdb9 100644 --- a/gfx/wr/webrender/src/texture_cache.rs +++ b/gfx/wr/webrender/src/texture_cache.rs @@ -493,6 +493,18 @@ impl SharedTextures { fn bytes_per_shared_texture(&self, budget_type: BudgetType) -> usize { self.bytes_per_texture_of_type[budget_type as usize] as usize } + + fn has_multiple_textures(&self, budget_type: BudgetType) -> bool { + match budget_type { + BudgetType::SharedColor8Linear => self.color8_linear.allocated_textures() > 1, + BudgetType::SharedColor8Nearest => self.color8_nearest.allocated_textures() > 1, + BudgetType::SharedColor8Glyphs => self.color8_glyphs.allocated_textures() > 1, + BudgetType::SharedAlpha8 => self.alpha8_linear.allocated_textures() > 1, + BudgetType::SharedAlpha8Glyphs => self.alpha8_glyphs.allocated_textures() > 1, + BudgetType::SharedAlpha16 => self.alpha16_linear.allocated_textures() > 1, + BudgetType::Standalone => false, + } + } } /// Container struct for the various parameters used in cache allocation. @@ -1087,50 +1099,105 @@ impl TextureCache { // to save GPU memory. Batching / draw call concerns do not apply // to standalone textures, because unused textures don't cause // extra draw calls. - return 16 * 1024 * 1024; + return 8 * 1024 * 1024; } // For shared textures, evicting an entry only frees up GPU memory if it - // causes one of the shared textures to become empty. - // The bigger concern for shared textures is batching: The entries that + // causes one of the shared textures to become empty, so we want to avoid + // getting slightly above the capacity of a texture. + // The other concern for shared textures is batching: The entries that // are needed in the current frame should be distributed across as few // shared textures as possible, to minimize the number of draw calls. - // Ideally we only want one or two textures per type. - let expected_texture_count = match budget_type { - BudgetType::SharedColor8Nearest | BudgetType::SharedAlpha16 => { - // These types are only rarely used, we don't want more than - // one of each. - 1 - }, + // Ideally we only want one texture per type under simple workloads. + let bytes_per_texture = self.shared_textures.bytes_per_shared_texture(budget_type); + + // Number of allocated bytes under which we don't bother with evicting anything + // from the cache. Above the threshold we consider evicting the coldest items + // depending on how cold they are. + // + // Above all else we want to make sure that even after a heavy workload, the + // shared cache settles back to a single texture atlas per type over some reasonable + // period of time. + // This is achieved by the compaction logic which will try to consolidate items that + // are spread over multiple textures into few ones, and by evicting old items + // so that the compaction logic has room to do its job. + // + // The other goal is to leave enough empty space in the texture atlases + // so that we are not too likely to have to allocate a new texture atlas on + // the next frame if we switch to a new tab or load a new page. That's why + // the following thresholds are rather low. Note that even when above the threshold, + // we only evict cold items and ramp up the eviction pressure depending on the amount + // of allocated memory (See should_continue_evicting). + let ideal_utilization = match budget_type { + BudgetType::SharedAlpha8Glyphs | BudgetType::SharedColor8Glyphs => { + // Glyphs are usually small and tightly packed so they waste very little + // space in the cache. + bytes_per_texture * 2 / 3 + } _ => { - // For the other types, having two textures is acceptable. - 2 - }, + // Other types of images come with a variety of sizes making them more + // prone to wasting pixels and causing fragmentation issues so we put + // more pressure on them. + bytes_per_texture / 3 + } }; - // The threshold that we pick here will be compared to the number of - // bytes that are *occupied by entries*. And we're trying to target a - // certain number of textures. - // Unfortunately, it's hard to predict the number of needed textures - // purely based on number of occupied bytes: Due to the different - // rectangular shape of each entry, and due to decisions made by the - // allocator, sometimes we may need a new texture even if there are - // still large gaps in the existing textures. - // Let's assume that we have an average allocator wastage of 50%. - let average_used_bytes_per_texture_when_full = - self.shared_textures.bytes_per_shared_texture(budget_type) / 2; - - // Compute the threshold. - // Because of varying allocator wastage, we may still need to use more - // than the expected number of textures; that's fine. We'll also go over - // the expected texture count whenever a large number of entries are - // needed to draw a complex frame (since we don't evict entries which - // are needed for the current frame), or if eviction hasn't had a chance - // to catch up after a large allocation burst. - expected_texture_count * average_used_bytes_per_texture_when_full + ideal_utilization } + /// Returns whether to continue eviction and how cold an item need to be to be evicted. + /// + /// If the None is returned, stop evicting. + /// If the Some(n) is returned, continue evicting if the coldest item hasn't been used + /// for more than n frames. + fn should_continue_evicting( + &self, + budget_type: BudgetType, + eviction_count: usize, + ) -> Option { + + let threshold = self.get_eviction_threshold(budget_type); + let bytes_allocated = self.bytes_allocated[budget_type as usize]; + + let uses_multiple_atlases = self.shared_textures.has_multiple_textures(budget_type); + + // If current memory usage is below selected threshold, we can stop evicting items + // except when using shared texture atlases and more than one texture is in use. + // This is not very common but can happen due to fragmentation and the only way + // to get rid of that fragmentation is to continue evicting. + if bytes_allocated < threshold && !uses_multiple_atlases { + return None; + } + + // Number of frames since last use that is considered too recent for eviction, + // depending on the cache pressure. + let age_theshold = match bytes_allocated / threshold { + 0 => 400, + 1 => 200, + 2 => 100, + 3 => 50, + 4 => 25, + 5 => 10, + 6 => 5, + _ => 1, + }; + + // If current memory usage is significantly more than the threshold, keep evicting this frame + if bytes_allocated > 4 * threshold { + return Some(age_theshold); + } + + // Otherwise, only allow evicting up to a certain number of items per frame. This allows evictions + // to be spread over a number of frames, to avoid frame spikes. + if eviction_count < Self::MAX_EVICTIONS_PER_FRAME { + return Some(age_theshold) + } + + None + } + + /// Evict old items from the shared and standalone caches, if we're over a /// threshold memory usage value fn evict_items_from_cache_if_required(&mut self, profile: &mut TransactionProfile) { @@ -1139,17 +1206,15 @@ impl TextureCache { let mut youngest_evicted = FrameId::first(); for budget in BudgetType::iter() { - let threshold = self.get_eviction_threshold(budget); - while self.should_continue_evicting( - self.bytes_allocated[budget as usize], - threshold, + while let Some(age_threshold) = self.should_continue_evicting( + budget, eviction_count, ) { if let Some(entry) = self.lru_cache.peek_oldest(budget as u8) { // Only evict this item if it wasn't used in the previous frame. The reason being that if it // was used the previous frame then it will likely be used in this frame too, and we don't // want to be continually evicting and reuploading the item every frame. - if entry.last_access.frame_id() >= previous_frame_id { + if entry.last_access.frame_id() + age_threshold > previous_frame_id { // Since the LRU cache is ordered by frame access, we can break out of the loop here because // we know that all remaining items were also used in the previous frame (or more recently). break; @@ -1180,28 +1245,6 @@ impl TextureCache { } } - /// Returns true if texture cache eviction loop should continue - fn should_continue_evicting( - &self, - bytes_allocated: usize, - threshold: usize, - eviction_count: usize, - ) -> bool { - // If current memory usage is below selected threshold, we can stop evicting items - if bytes_allocated < threshold { - return false; - } - - // If current memory usage is significantly more than the threshold, keep evicting this frame - if bytes_allocated > 4 * threshold { - return true; - } - - // Otherwise, only allow evicting up to a certain number of items per frame. This allows evictions - // to be spread over a number of frames, to avoid frame spikes. - eviction_count < Self::MAX_EVICTIONS_PER_FRAME - } - // Free a cache entry from the standalone list or shared cache. fn free(&mut self, entry: &CacheEntry) { match entry.details { From 127a09cc61bb81fa08b83ae9342ba8ea717bab9c Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 21 Oct 2021 08:54:51 +0000 Subject: [PATCH 07/35] Bug 1711477 - Use a dedicated shader for copying from a texture to another. r=gfx-reviewers,kvark Using the composite shader for that was very clunky. It might even be faster thanks to how much simpler the shader is and it's use of texel fetch instead of linear sampling. Differential Revision: https://phabricator.services.mozilla.com/D128256 --- gfx/wr/webrender/res/ps_copy.glsl | 41 +++++++++ gfx/wr/webrender/src/gpu_types.rs | 10 +++ gfx/wr/webrender/src/renderer/mod.rs | 86 ++++--------------- gfx/wr/webrender/src/renderer/shade.rs | 18 +++- gfx/wr/webrender/src/renderer/upload.rs | 80 +++++------------ gfx/wr/webrender/src/renderer/vertex.rs | 30 +++++++ gfx/wr/webrender_build/src/shader_features.rs | 2 + 7 files changed, 140 insertions(+), 127 deletions(-) create mode 100644 gfx/wr/webrender/res/ps_copy.glsl diff --git a/gfx/wr/webrender/res/ps_copy.glsl b/gfx/wr/webrender/res/ps_copy.glsl new file mode 100644 index 0000000000000..7bcb469b5c2bc --- /dev/null +++ b/gfx/wr/webrender/res/ps_copy.glsl @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include base + +#ifdef WR_VERTEX_SHADER + +attribute vec2 aPosition; + +// See CopyInstance struct. +attribute vec4 a_src_rect; +attribute vec4 a_dst_rect; +attribute vec2 a_dst_texture_size; + +varying vec2 v_uv; + +void main(void) { + // We use texel fetch so v_uv is in unnormalized device space. + v_uv = mix(a_src_rect.xy, a_src_rect.zw, aPosition.xy); + + // Transform into framebuffer [-1, 1] space. + vec2 pos = mix(a_dst_rect.xy, a_dst_rect.zw, aPosition.xy); + gl_Position = vec4(pos / (a_dst_texture_size * 0.5) - vec2(1.0, 1.0), 0.0, 1.0); +} +#endif + +#ifdef WR_FRAGMENT_SHADER + + +out vec4 oFragColor; + +varying vec2 v_uv; + +uniform sampler2D sColor0; + +void main(void) { + oFragColor = texelFetch(sColor0, ivec2(v_uv), 0); +} + +#endif diff --git a/gfx/wr/webrender/src/gpu_types.rs b/gfx/wr/webrender/src/gpu_types.rs index 0980f3b94371a..3a016958623c2 100644 --- a/gfx/wr/webrender/src/gpu_types.rs +++ b/gfx/wr/webrender/src/gpu_types.rs @@ -57,6 +57,16 @@ impl ZBufferIdGenerator { } } +#[derive(Clone, Debug)] +#[repr(C)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct CopyInstance { + pub src_rect: DeviceRect, + pub dst_rect: DeviceRect, + pub dst_texture_size: DeviceSize, +} + #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs index a8482876c16d6..c7c778059e229 100644 --- a/gfx/wr/webrender/src/renderer/mod.rs +++ b/gfx/wr/webrender/src/renderer/mod.rs @@ -71,7 +71,7 @@ use crate::glyph_cache::GlyphCache; use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterizer}; use crate::gpu_cache::{GpuCacheUpdate, GpuCacheUpdateList}; use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd}; -use crate::gpu_types::{PrimitiveInstanceData, ScalingInstance, SvgFilterInstance}; +use crate::gpu_types::{PrimitiveInstanceData, ScalingInstance, SvgFilterInstance, CopyInstance}; use crate::gpu_types::{BlurInstance, ClearInstance, CompositeInstance, ZBufferId, CompositorTransform}; use crate::internal_types::{TextureSource, ResourceCacheError, TextureCacheCategory, FrameId}; #[cfg(any(feature = "capture", feature = "replay"))] @@ -2251,88 +2251,40 @@ impl Renderer { for update_list in pending_texture_updates.drain(..) { // Handle copies from one texture to another. for ((src_tex, dst_tex), copies) in &update_list.copies { - let mut copy_instances = Vec::new(); - let src_texture = &self.texture_resolver.texture_cache_map[&src_tex].texture; let dest_texture = &self.texture_resolver.texture_cache_map[&dst_tex].texture; + let dst_texture_size = dest_texture.get_dimensions().to_f32(); - self.device.bind_texture( - TextureSampler::Color0, - src_texture, - Swizzle::default(), - ); - - let target_size = dest_texture.get_dimensions(); + let mut copy_instances = Vec::new(); + for copy in copies { + copy_instances.push(CopyInstance { + src_rect: copy.src_rect.to_f32(), + dst_rect: copy.dst_rect.to_f32(), + dst_texture_size, + }); + } - let draw_target = DrawTarget::from_texture( - dest_texture, - false, - ); + let draw_target = DrawTarget::from_texture(dest_texture, false); self.device.bind_draw_target(draw_target); - let projection = Transform3D::ortho( - 0.0, - target_size.width as f32, - 0.0, - target_size.height as f32, - self.device.ortho_near_plane(), - self.device.ortho_far_plane(), - ); - self.shaders .borrow_mut() - .get_composite_shader( - CompositeSurfaceFormat::Rgba, - ImageBufferKind::Texture2D, - CompositeFeatures::empty(), - ).bind( + .ps_copy + .bind( &mut self.device, - &projection, + &Transform3D::identity(), None, &mut self.renderer_errors, &mut self.profile, ); - for copy in copies { - let uv_rect = TexelRect::new( - copy.src_rect.min.x as f32, - copy.src_rect.min.y as f32, - copy.src_rect.max.x as f32, - copy.src_rect.max.y as f32, - ); - - copy_instances.push(CompositeInstance::new_rgb( - copy.dst_rect.to_f32().cast_unit(), - copy.dst_rect.to_f32(), - PremultipliedColorF::WHITE, - ZBufferId(0), - uv_rect, - CompositorTransform::identity(), - )); - } - - let mut dummy_stats = RendererStats { - total_draw_calls: 0, - alpha_target_count: 0, - color_target_count: 0, - texture_upload_mb: 0.0, - resource_upload_time: 0.0, - gpu_cache_upload_time: 0.0, - gecko_display_list_time: 0.0, - wr_display_list_time: 0.0, - scene_build_time: 0.0, - frame_build_time: 0.0, - full_display_list: false, - full_paint: false, - }; - self.draw_instanced_batch( ©_instances, - VertexArrayKind::Composite, - // We bind the staging texture manually because it isn't known - // to the texture resolver. - &BatchTextures::empty(), - &mut dummy_stats, + VertexArrayKind::Copy, + &BatchTextures::composite_rgb( + TextureSource::TextureCache(*src_tex, Swizzle::default()) + ), + &mut RendererStats::default(), ); } diff --git a/gfx/wr/webrender/src/renderer/shade.rs b/gfx/wr/webrender/src/renderer/shade.rs index 44d794e0ad4b8..eb5ff2eec3633 100644 --- a/gfx/wr/webrender/src/renderer/shade.rs +++ b/gfx/wr/webrender/src/renderer/shade.rs @@ -79,6 +79,7 @@ pub(crate) enum ShaderKind { Resolve, Composite, Clear, + Copy, } pub struct LazilyCompiledShader { @@ -173,7 +174,7 @@ impl LazilyCompiledShader { if self.program.is_none() { let start_time = precise_time_ns(); let program = match self.kind { - ShaderKind::Primitive | ShaderKind::Brush | ShaderKind::Text | ShaderKind::Resolve | ShaderKind::Clear => { + ShaderKind::Primitive | ShaderKind::Brush | ShaderKind::Text | ShaderKind::Resolve | ShaderKind::Clear | ShaderKind::Copy => { create_prim_shader( self.name, device, @@ -238,6 +239,7 @@ impl LazilyCompiledShader { ShaderKind::Resolve => VertexArrayKind::Resolve, ShaderKind::Composite => VertexArrayKind::Composite, ShaderKind::Clear => VertexArrayKind::Clear, + ShaderKind::Copy => VertexArrayKind::Copy, }; let vertex_descriptor = match vertex_format { @@ -259,6 +261,7 @@ impl LazilyCompiledShader { VertexArrayKind::SvgFilter => &desc::SVG_FILTER, VertexArrayKind::Composite => &desc::COMPOSITE, VertexArrayKind::Clear => &desc::CLEAR, + VertexArrayKind::Copy => &desc::COPY, }; device.link_program(program, vertex_descriptor)?; @@ -622,6 +625,7 @@ pub struct Shaders { ps_split_composite: LazilyCompiledShader, pub ps_clear: LazilyCompiledShader, + pub ps_copy: LazilyCompiledShader, // Composite shaders. These are very simple shaders used to composite // picture cache tiles into the framebuffer on platforms that do not have an @@ -898,6 +902,16 @@ impl Shaders { profile, )?; + let ps_copy = LazilyCompiledShader::new( + ShaderKind::Copy, + "ps_copy", + &[], + device, + options.precache_flags, + &shader_list, + profile, + )?; + // All image configuration. let mut image_features = Vec::new(); let mut brush_image = Vec::new(); @@ -1145,6 +1159,7 @@ impl Shaders { ps_text_run_dual_source, ps_split_composite, ps_clear, + ps_copy, composite_rgba, composite_rgba_fast_path, composite_yuv, @@ -1332,6 +1347,7 @@ impl Shaders { self.cs_border_segment.deinit(device); self.ps_split_composite.deinit(device); self.ps_clear.deinit(device); + self.ps_copy.deinit(device); for shader in self.composite_rgba { if let Some(shader) = shader { diff --git a/gfx/wr/webrender/src/renderer/upload.rs b/gfx/wr/webrender/src/renderer/upload.rs index 63625ee083e53..720389bb1f49c 100644 --- a/gfx/wr/webrender/src/renderer/upload.rs +++ b/gfx/wr/webrender/src/renderer/upload.rs @@ -23,7 +23,7 @@ use euclid::{Transform3D, point2}; use time::precise_time_ns; use malloc_size_of::MallocSizeOfOps; use api::units::*; -use api::{ExternalImageSource, PremultipliedColorF, ImageBufferKind, ImageRendering, ImageFormat}; +use api::{ExternalImageSource, ImageBufferKind, ImageRendering, ImageFormat}; use crate::renderer::{ Renderer, VertexArrayKind, RendererStats, TextureSampler, TEXTURE_CACHE_DBG_CLEAR_COLOR }; @@ -35,10 +35,9 @@ use crate::device::{ Device, UploadMethod, Texture, DrawTarget, UploadStagingBuffer, TextureFlags, TextureUploader, TextureFilter, }; -use crate::gpu_types::{ZBufferId, CompositeInstance, CompositorTransform}; +use crate::gpu_types::CopyInstance; use crate::batch::BatchTextures; use crate::texture_pack::{GuillotineAllocator, FreeRectSlice}; -use crate::composite::{CompositeFeatures, CompositeSurfaceFormat}; use crate::profiler; use crate::render_api::MemoryReport; @@ -527,24 +526,10 @@ fn copy_from_staging_to_cache_using_draw_calls( batch_upload_textures: &[Texture], batch_upload_copies: Vec, ) { - let mut dummy_stats = RendererStats { - total_draw_calls: 0, - alpha_target_count: 0, - color_target_count: 0, - texture_upload_mb: 0.0, - resource_upload_time: 0.0, - gpu_cache_upload_time: 0.0, - gecko_display_list_time: 0.0, - wr_display_list_time: 0.0, - scene_build_time: 0.0, - frame_build_time: 0.0, - full_display_list: false, - full_paint: false, - }; - let mut copy_instances = Vec::new(); let mut prev_src = None; let mut prev_dst = None; + let mut dst_texture_size = DeviceSize::new(0.0, 0.0); for copy in batch_upload_copies { @@ -552,14 +537,13 @@ fn copy_from_staging_to_cache_using_draw_calls( let dst_changed = prev_dst != Some(copy.dest_texture_id); if (src_changed || dst_changed) && !copy_instances.is_empty() { - renderer.draw_instanced_batch( ©_instances, - VertexArrayKind::Composite, + VertexArrayKind::Copy, // We bind the staging texture manually because it isn't known // to the texture resolver. &BatchTextures::empty(), - &mut dummy_stats, + &mut RendererStats::default(), ); stats.num_draw_calls += 1; @@ -568,32 +552,17 @@ fn copy_from_staging_to_cache_using_draw_calls( if dst_changed { let dest_texture = &renderer.texture_resolver.texture_cache_map[©.dest_texture_id].texture; - let target_size = dest_texture.get_dimensions(); + dst_texture_size = dest_texture.get_dimensions().to_f32(); - let draw_target = DrawTarget::from_texture( - dest_texture, - false, - ); + let draw_target = DrawTarget::from_texture(dest_texture, false); renderer.device.bind_draw_target(draw_target); - let projection = Transform3D::ortho( - 0.0, - target_size.width as f32, - 0.0, - target_size.height as f32, - renderer.device.ortho_near_plane(), - renderer.device.ortho_far_plane(), - ); - renderer.shaders .borrow_mut() - .get_composite_shader( - CompositeSurfaceFormat::Rgba, - ImageBufferKind::Texture2D, - CompositeFeatures::empty(), - ).bind( + .ps_copy + .bind( &mut renderer.device, - &projection, + &Transform3D::identity(), None, &mut renderer.renderer_errors, &mut renderer.profile, @@ -612,36 +581,29 @@ fn copy_from_staging_to_cache_using_draw_calls( prev_src = Some(copy.src_texture_index) } - let dest_rect = DeviceRect::from_origin_and_size( - copy.dest_offset.to_f32(), + let src_rect = DeviceRect::from_origin_and_size( + copy.src_offset.to_f32(), copy.size.to_f32(), ); - let src_rect = TexelRect::new( - copy.src_offset.x as f32, - copy.src_offset.y as f32, - (copy.src_offset.x + copy.size.width) as f32, - (copy.src_offset.y + copy.size.height) as f32, + let dst_rect = DeviceRect::from_origin_and_size( + copy.dest_offset.to_f32(), + copy.size.to_f32(), ); - copy_instances.push(CompositeInstance::new_rgb( - dest_rect.cast_unit(), - dest_rect, - PremultipliedColorF::WHITE, - ZBufferId(0), + copy_instances.push(CopyInstance { src_rect, - CompositorTransform::identity(), - )); + dst_rect, + dst_texture_size, + }); } if !copy_instances.is_empty() { renderer.draw_instanced_batch( ©_instances, - VertexArrayKind::Composite, - // We bind the staging texture manually because it isn't known - // to the texture resolver. + VertexArrayKind::Copy, &BatchTextures::empty(), - &mut dummy_stats, + &mut RendererStats::default(), ); stats.num_draw_calls += 1; diff --git a/gfx/wr/webrender/src/renderer/vertex.rs b/gfx/wr/webrender/src/renderer/vertex.rs index de60947261faa..2f871b4c2663b 100644 --- a/gfx/wr/webrender/src/renderer/vertex.rs +++ b/gfx/wr/webrender/src/renderer/vertex.rs @@ -758,6 +758,31 @@ pub mod desc { }, ], }; + + pub const COPY: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::U8Norm, + }], + instance_attributes: &[ + VertexAttribute { + name: "a_src_rect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "a_dst_rect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "a_dst_texture_size", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + }; } #[derive(Debug, Copy, Clone, PartialEq)] @@ -780,6 +805,7 @@ pub enum VertexArrayKind { SvgFilter, Composite, Clear, + Copy, } pub struct VertexDataTexture { @@ -1003,6 +1029,7 @@ pub struct RendererVAOs { svg_filter_vao: VAO, composite_vao: VAO, clear_vao: VAO, + copy_vao: VAO, } impl RendererVAOs { @@ -1049,6 +1076,7 @@ impl RendererVAOs { svg_filter_vao: device.create_vao_with_new_instances(&desc::SVG_FILTER, &prim_vao), composite_vao: device.create_vao_with_new_instances(&desc::COMPOSITE, &prim_vao), clear_vao: device.create_vao_with_new_instances(&desc::CLEAR, &prim_vao), + copy_vao: device.create_vao_with_new_instances(&desc::COPY, &prim_vao), prim_vao, } } @@ -1070,6 +1098,7 @@ impl RendererVAOs { device.delete_vao(self.svg_filter_vao); device.delete_vao(self.composite_vao); device.delete_vao(self.clear_vao); + device.delete_vao(self.copy_vao); } } @@ -1094,6 +1123,7 @@ impl ops::Index for RendererVAOs { VertexArrayKind::SvgFilter => &self.svg_filter_vao, VertexArrayKind::Composite => &self.composite_vao, VertexArrayKind::Clear => &self.clear_vao, + VertexArrayKind::Copy => &self.copy_vao, } } } diff --git a/gfx/wr/webrender_build/src/shader_features.rs b/gfx/wr/webrender_build/src/shader_features.rs index 19c4ebb45f184..1b9e0c682ca76 100644 --- a/gfx/wr/webrender_build/src/shader_features.rs +++ b/gfx/wr/webrender_build/src/shader_features.rs @@ -223,6 +223,8 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures { shaders.insert("ps_clear", vec![base_prim_features.finish()]); + shaders.insert("ps_copy", vec![base_prim_features.finish()]); + if flags.contains(ShaderFeatureFlags::DEBUG) { for name in &["debug_color", "debug_font"] { shaders.insert(name, vec![String::new()]); From f3173845680968211bc30fa79c04a98e7bbf1f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 21 Oct 2021 09:07:12 +0000 Subject: [PATCH 08/35] Bug 1736518 - Make browser.tabs.drawInTitlebar a tri-state. r=stransky,Gijs To more properly support Linux having a different default at runtime. Expose the resolved value in appinfo for convenience, and use it in the front-end as needed. Differential Revision: https://phabricator.services.mozilla.com/D129004 --- browser/app/profile/firefox.js | 5 --- .../base/content/browser-tabsintitlebar.js | 5 +-- .../content/test/popups/browser_popupUI.js | 6 +-- .../browser_preXULSkeletonUIRegistry.js | 2 +- browser/components/BrowserGlue.jsm | 5 +-- .../customizableui/CustomizableUI.jsm | 10 ++--- .../customizableui/CustomizeMode.jsm | 7 +--- .../browser_970511_undo_restore_default.js | 42 +++++++++++++------ browser/modules/BrowserUsageTelemetry.jsm | 11 +---- .../browser_UsageTelemetry_toolbars.js | 2 +- .../configurations/TabsInTitlebar.jsm | 7 +--- layout/style/test/chrome/bug418986-2.js | 1 - .../test/chrome/chrome-only-media-queries.js | 1 - modules/libpref/init/StaticPrefList.yaml | 8 ++++ .../components/style/gecko/media_features.rs | 3 +- toolkit/xre/nsAppRunner.cpp | 15 +++++-- widget/LookAndFeel.h | 11 +++-- widget/gtk/nsLookAndFeel.cpp | 28 ++++++++++--- widget/gtk/nsLookAndFeel.h | 3 +- widget/gtk/nsWindow.cpp | 23 ---------- widget/gtk/nsWindow.h | 1 - widget/headless/HeadlessLookAndFeelGTK.cpp | 1 - widget/nsXPLookAndFeel.cpp | 13 +++++- widget/nsXPLookAndFeel.h | 2 + xpcom/ds/StaticAtoms.py | 1 - xpcom/system/nsIXULRuntime.idl | 3 ++ 26 files changed, 112 insertions(+), 104 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index a9e1e937aae4e..cc1d9701c633f 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -629,11 +629,6 @@ pref("browser.tabs.tabMinWidth", 76); // secondary text on tabs hidden due to size constraints and readability // of the text at small font sizes. pref("browser.tabs.secondaryTextUnsupportedLocales", "ar,bn,bo,ckb,fa,gu,he,hi,ja,km,kn,ko,lo,mr,my,ne,pa,si,ta,te,th,ur,zh"); -// Initial titlebar state is managed by -moz-gtk-csd-hide-titlebar-by-default -// on Linux. -#ifndef UNIX_BUT_NOT_MAC - pref("browser.tabs.drawInTitlebar", true); -#endif //Control the visibility of Tab Manager Menu. pref("browser.tabs.tabmanager.enabled", false); diff --git a/browser/base/content/browser-tabsintitlebar.js b/browser/base/content/browser-tabsintitlebar.js index 23301332d8426..cf11dbdbe9fd3 100644 --- a/browser/base/content/browser-tabsintitlebar.js +++ b/browser/base/content/browser-tabsintitlebar.js @@ -54,10 +54,7 @@ var TabsInTitlebar = { _prefName: "browser.tabs.drawInTitlebar", _readPref() { - let hiddenTitlebar = Services.prefs.getBoolPref( - "browser.tabs.drawInTitlebar", - window.matchMedia("(-moz-gtk-csd-hide-titlebar-by-default)").matches - ); + let hiddenTitlebar = Services.appinfo.drawInTitlebar; this.allowedBy("pref", hiddenTitlebar); }, diff --git a/browser/base/content/test/popups/browser_popupUI.js b/browser/base/content/test/popups/browser_popupUI.js index 6b65cee57a271..237636b13cc41 100644 --- a/browser/base/content/test/popups/browser_popupUI.js +++ b/browser/base/content/test/popups/browser_popupUI.js @@ -61,8 +61,8 @@ add_task(async function titlebar_buttons_visibility() { Services.prefs.setIntPref("browser.link.open_newwindow", 2); const drawInTitlebarValues = [ - [true, BUTTONS_MAY_VISIBLE], - [false, BUTTONS_NEVER_VISIBLE], + [1, BUTTONS_MAY_VISIBLE], + [0, BUTTONS_NEVER_VISIBLE], ]; const windowFeaturesValues = [ // Opens a popup @@ -77,7 +77,7 @@ add_task(async function titlebar_buttons_visibility() { const menuBarShownValues = [true, false]; for (const [drawInTitlebar, drawInTitlebarButtons] of drawInTitlebarValues) { - Services.prefs.setBoolPref("browser.tabs.drawInTitlebar", drawInTitlebar); + Services.prefs.setIntPref("browser.tabs.drawInTitlebar", drawInTitlebar); for (const [ windowFeatures, diff --git a/browser/base/content/test/startup/browser_preXULSkeletonUIRegistry.js b/browser/base/content/test/startup/browser_preXULSkeletonUIRegistry.js index 4a18807eb7261..8afc7d7ae1237 100644 --- a/browser/base/content/test/startup/browser_preXULSkeletonUIRegistry.js +++ b/browser/base/content/test/startup/browser_preXULSkeletonUIRegistry.js @@ -71,7 +71,7 @@ add_task(async function testWritesEnabledOnPrefChange() { is(enabled, 0, "Pre-XUL skeleton UI is disabled in the Windows registry"); Services.prefs.setBoolPref("browser.startup.preXulSkeletonUI", true); - Services.prefs.setBoolPref("browser.tabs.drawInTitlebar", false); + Services.prefs.setIntPref("browser.tabs.drawInTitlebar", 0); enabled = WindowsRegistry.readRegKey( Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, "Software\\Mozilla\\Firefox\\PreXULSkeletonUISettings", diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index 9bdab2335eb2f..4772993ae9216 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -800,10 +800,7 @@ let JSWINDOWACTORS = { ); // Hide the titlebar if the actual browser window will draw in it. - let hiddenTitlebar = Services.prefs.getBoolPref( - "browser.tabs.drawInTitlebar", - win.matchMedia("(-moz-gtk-csd-hide-titlebar-by-default)").matches - ); + let hiddenTitlebar = Services.appinfo.drawInTitlebar; if (hiddenTitlebar) { win.windowUtils.setChromeMargin(0, 2, 2, 2); } diff --git a/browser/components/customizableui/CustomizableUI.jsm b/browser/components/customizableui/CustomizableUI.jsm index de7c20f15fb46..810ca51afa211 100644 --- a/browser/components/customizableui/CustomizableUI.jsm +++ b/browser/components/customizableui/CustomizableUI.jsm @@ -3151,12 +3151,8 @@ var CustomizableUIInternal = { _resetUIState() { try { - // kPrefDrawInTitlebar may not be defined on Linux/Gtk+ which throws an exception - // and leads to whole test failure. Let's set a fallback default value to avoid that, - // both titlebar states are tested anyway and it's not important which state is tested first. - gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref( - kPrefDrawInTitlebar, - false + gUIStateBeforeReset.drawInTitlebar = Services.prefs.getIntPref( + kPrefDrawInTitlebar ); gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref( kPrefCustomizationState @@ -3246,7 +3242,7 @@ var CustomizableUIInternal = { this._clearPreviousUIState(); Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState); - Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar); + Services.prefs.setIntPref(kPrefDrawInTitlebar, drawInTitlebar); Services.prefs.setIntPref(kPrefUIDensity, uiDensity); Services.prefs.setBoolPref(kPrefAutoTouchMode, autoTouchMode); Services.prefs.setBoolPref( diff --git a/browser/components/customizableui/CustomizeMode.jsm b/browser/components/customizableui/CustomizeMode.jsm index d689a39fb5fc4..9a88875a7d6ab 100644 --- a/browser/components/customizableui/CustomizeMode.jsm +++ b/browser/components/customizableui/CustomizeMode.jsm @@ -1715,10 +1715,7 @@ CustomizeMode.prototype = { }, _updateTitlebarCheckbox() { - let drawInTitlebar = Services.prefs.getBoolPref( - kDrawInTitlebarPref, - this.window.matchMedia("(-moz-gtk-csd-hide-titlebar-by-default)").matches - ); + let drawInTitlebar = Services.appinfo.drawInTitlebar; let checkbox = this.$("customization-titlebar-visibility-checkbox"); // Drawing in the titlebar means 'hiding' the titlebar. // We use the attribute rather than a property because if we're not in @@ -1732,7 +1729,7 @@ CustomizeMode.prototype = { toggleTitlebar(aShouldShowTitlebar) { // Drawing in the titlebar means not showing the titlebar, hence the negation: - Services.prefs.setBoolPref(kDrawInTitlebarPref, !aShouldShowTitlebar); + Services.prefs.setIntPref(kDrawInTitlebarPref, !aShouldShowTitlebar); }, _getBoundsWithoutFlushing(element) { diff --git a/browser/components/customizableui/test/browser_970511_undo_restore_default.js b/browser/components/customizableui/test/browser_970511_undo_restore_default.js index 89ee03947f17c..e662d5876fd51 100644 --- a/browser/components/customizableui/test/browser_970511_undo_restore_default.js +++ b/browser/components/customizableui/test/browser_970511_undo_restore_default.js @@ -142,11 +142,16 @@ add_task(async function() { // Bug 971626 - Restore Defaults should collapse the Title Bar add_task(async function() { - if (Services.appinfo.OS != "WINNT" && Services.appinfo.OS != "Darwin") { - return; + { + const supported = TabsInTitlebar.systemSupported; + is(typeof supported, "boolean"); + info("TabsInTitlebar support: " + supported); + if (!supported) { + return; + } } - let prefName = "browser.tabs.drawInTitlebar"; - let defaultValue = Services.prefs.getBoolPref(prefName); + + const kDefaultValue = Services.appinfo.drawInTitlebar; let restoreDefaultsButton = document.getElementById( "customization-reset-button" ); @@ -166,7 +171,7 @@ add_task(async function() { ); is( titlebarCheckbox.hasAttribute("checked"), - !defaultValue, + !kDefaultValue, "Title bar checkbox should reflect pref value" ); is( @@ -175,14 +180,20 @@ add_task(async function() { "Undo reset button should be hidden at start of test" ); - Services.prefs.setBoolPref(prefName, !defaultValue); + let prefName = "browser.tabs.drawInTitlebar"; + Services.prefs.setIntPref(prefName, !kDefaultValue); ok( !restoreDefaultsButton.disabled, "Restore defaults button should be enabled when pref changed" ); + is( + Services.appinfo.drawInTitlebar, + !kDefaultValue, + "Title bar checkbox should reflect changed pref value" + ); is( titlebarCheckbox.hasAttribute("checked"), - defaultValue, + kDefaultValue, "Title bar checkbox should reflect changed pref value" ); ok( @@ -202,14 +213,19 @@ add_task(async function() { ); is( titlebarCheckbox.hasAttribute("checked"), - !defaultValue, + !kDefaultValue, "Title bar checkbox should reflect default value after reset" ); is( - Services.prefs.getBoolPref(prefName), - defaultValue, + Services.prefs.getIntPref(prefName), + 2, "Reset should reset drawInTitlebar" ); + is( + Services.appinfo.drawInTitlebar, + kDefaultValue, + "Default state should be restored" + ); ok(CustomizableUI.inDefaultState, "In default state after titlebar reset"); is( undoResetButton.hidden, @@ -228,13 +244,13 @@ add_task(async function() { ); is( titlebarCheckbox.hasAttribute("checked"), - defaultValue, + kDefaultValue, "Title bar checkbox should reflect undo-reset value" ); ok(!CustomizableUI.inDefaultState, "No longer in default state after undo"); is( - Services.prefs.getBoolPref(prefName), - !defaultValue, + Services.prefs.getIntPref(prefName), + kDefaultValue ? 0 : 1, "Undo-reset goes back to previous pref value" ); is( diff --git a/browser/modules/BrowserUsageTelemetry.jsm b/browser/modules/BrowserUsageTelemetry.jsm index ab7a70a71f109..85305e879d82e 100644 --- a/browser/modules/BrowserUsageTelemetry.jsm +++ b/browser/modules/BrowserUsageTelemetry.jsm @@ -497,9 +497,7 @@ let BrowserUsageTelemetry = { case "browser.tabs.drawInTitlebar": this._recordWidgetChange( "titlebar", - Services.prefs.getBoolPref("browser.tabs.drawInTitlebar") - ? "off" - : "on", + Services.appinfo.drawInTitlebar ? "off" : "on", "pref" ); break; @@ -601,12 +599,7 @@ let BrowserUsageTelemetry = { widgetMap.set("menu-toolbar", menuBarHidden ? "off" : "on"); // Drawing in the titlebar means not showing the titlebar, hence the negation. - widgetMap.set( - "titlebar", - Services.prefs.getBoolPref("browser.tabs.drawInTitlebar", true) - ? "off" - : "on" - ); + widgetMap.set("titlebar", Services.appinfo.drawInTitlebar ? "off" : "on"); for (let area of CustomizableUI.areas) { if (!(area in BROWSER_UI_CONTAINER_IDS)) { diff --git a/browser/modules/test/browser/browser_UsageTelemetry_toolbars.js b/browser/modules/test/browser/browser_UsageTelemetry_toolbars.js index 4f4b4578764fe..db9208a61aa7e 100644 --- a/browser/modules/test/browser/browser_UsageTelemetry_toolbars.js +++ b/browser/modules/test/browser/browser_UsageTelemetry_toolbars.js @@ -111,7 +111,7 @@ function organizeToolbars(state = {}) { targetState.personalToolbarVisible ); - Services.prefs.setBoolPref( + Services.prefs.setIntPref( "browser.tabs.drawInTitlebar", !targetState.titlebarVisible ); diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/TabsInTitlebar.jsm b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/TabsInTitlebar.jsm index a728c9764fcf6..06578da776f59 100644 --- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/TabsInTitlebar.jsm +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/TabsInTitlebar.jsm @@ -17,10 +17,7 @@ var TabsInTitlebar = { tabsInTitlebar: { selectors: ["#navigator-toolbox"], async applyConfig() { - if (Services.appinfo.OS == "Linux") { - return "TabsInTitlebar isn't supported on Linux"; - } - Services.prefs.setBoolPref(PREF_TABS_IN_TITLEBAR, true); + Services.prefs.setIntPref(PREF_TABS_IN_TITLEBAR, 1); return undefined; }, }, @@ -30,7 +27,7 @@ var TabsInTitlebar = { Services.appinfo.OS == "Linux" ? [] : ["#titlebar"] ), async applyConfig() { - Services.prefs.setBoolPref(PREF_TABS_IN_TITLEBAR, false); + Services.prefs.setIntPref(PREF_TABS_IN_TITLEBAR, 0); }, }, }, diff --git a/layout/style/test/chrome/bug418986-2.js b/layout/style/test/chrome/bug418986-2.js index 9da3b9d86233f..8bca6f4b21508 100644 --- a/layout/style/test/chrome/bug418986-2.js +++ b/layout/style/test/chrome/bug418986-2.js @@ -61,7 +61,6 @@ var suppressed_toggles = [ "-moz-windows-default-theme", "-moz-windows-glass", "-moz-gtk-csd-available", - "-moz-gtk-csd-hide-titlebar-by-default", "-moz-gtk-csd-minimize-button", "-moz-gtk-csd-maximize-button", "-moz-gtk-csd-close-button", diff --git a/layout/style/test/chrome/chrome-only-media-queries.js b/layout/style/test/chrome/chrome-only-media-queries.js index 5ccde7bc9256f..605580a0988c1 100644 --- a/layout/style/test/chrome/chrome-only-media-queries.js +++ b/layout/style/test/chrome/chrome-only-media-queries.js @@ -21,7 +21,6 @@ const CHROME_ONLY_TOGGLES = [ "-moz-windows-non-native-menus", "-moz-swipe-animation-enabled", "-moz-gtk-csd-available", - "-moz-gtk-csd-hide-titlebar-by-default", "-moz-gtk-csd-minimize-button", "-moz-gtk-csd-maximize-button", "-moz-gtk-csd-close-button", diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 052c4f7db5cc2..7bc51a1479aab 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -1300,6 +1300,14 @@ value: false mirror: always +# Whether we should draw the tabs on top of the titlebar. +# +# no (0), yes (1), or default (2), which is true everywhere except Linux. +- name: browser.tabs.drawInTitlebar + type: int32_t + value: 2 + mirror: always + # If set, use DocumentChannel to directly initiate loads entirely # from parent-process BrowsingContexts - name: browser.tabs.documentchannel.parent-controlled diff --git a/servo/components/style/gecko/media_features.rs b/servo/components/style/gecko/media_features.rs index 34bcda32e1091..40a34095d6f68 100644 --- a/servo/components/style/gecko/media_features.rs +++ b/servo/components/style/gecko/media_features.rs @@ -651,7 +651,7 @@ macro_rules! bool_pref_feature { /// to support new types in these entries and (2) ensuring that either /// nsPresContext::MediaFeatureValuesChanged is called when the value that /// would be returned by the evaluator function could change. -pub static MEDIA_FEATURES: [MediaFeatureDescription; 59] = [ +pub static MEDIA_FEATURES: [MediaFeatureDescription; 58] = [ feature!( atom!("width"), AllowsRanges::Yes, @@ -890,7 +890,6 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 59] = [ lnf_int_feature!(atom!("-moz-windows-glass"), WindowsGlass), lnf_int_feature!(atom!("-moz-swipe-animation-enabled"), SwipeAnimationEnabled), lnf_int_feature!(atom!("-moz-gtk-csd-available"), GTKCSDAvailable), - lnf_int_feature!(atom!("-moz-gtk-csd-hide-titlebar-by-default"), GTKCSDHideTitlebarByDefault), lnf_int_feature!(atom!("-moz-gtk-csd-minimize-button"), GTKCSDMinimizeButton), lnf_int_feature!(atom!("-moz-gtk-csd-maximize-button"), GTKCSDMaximizeButton), lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton), diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index c9ea8cdc6e18c..5e18ea7672a00 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -23,6 +23,7 @@ #include "mozilla/Printf.h" #include "mozilla/ResultExtensions.h" #include "mozilla/ScopeExit.h" +#include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_fission.h" #include "mozilla/Telemetry.h" #include "mozilla/Utf8.h" @@ -276,7 +277,6 @@ static const char kPrefThemeId[] = "extensions.activeThemeID"; static const char kPrefBrowserStartupBlankWindow[] = "browser.startup.blankWindow"; static const char kPrefPreXulSkeletonUI[] = "browser.startup.preXulSkeletonUI"; -static const char kPrefDrawTabsInTitlebar[] = "browser.tabs.drawInTitlebar"; #endif // defined(XP_WIN) int gArgc; @@ -1331,6 +1331,12 @@ nsXULAppInfo::GetChromeColorSchemeIsDark(bool* aResult) { return NS_OK; } +NS_IMETHODIMP +nsXULAppInfo::GetDrawInTitlebar(bool* aResult) { + *aResult = LookAndFeel::DrawInTitlebar(); + return NS_OK; +} + NS_IMETHODIMP nsXULAppInfo::GetProcessStartupShortcut(nsAString& aShortcut) { #if defined(XP_WIN) @@ -1996,7 +2002,7 @@ static void ReflectSkeletonUIPrefToRegistry(const char* aPref, void* aData) { bool shouldBeEnabled = Preferences::GetBool(kPrefPreXulSkeletonUI, false) && Preferences::GetBool(kPrefBrowserStartupBlankWindow, false) && - Preferences::GetBool(kPrefDrawTabsInTitlebar, false); + LookAndFeel::DrawInTitlebar(); if (shouldBeEnabled && Preferences::HasUserValue(kPrefThemeId)) { nsCString themeId; Preferences::GetCString(kPrefThemeId, themeId); @@ -2025,8 +2031,9 @@ static void SetupSkeletonUIPrefs() { Preferences::RegisterCallback(&ReflectSkeletonUIPrefToRegistry, kPrefBrowserStartupBlankWindow); Preferences::RegisterCallback(&ReflectSkeletonUIPrefToRegistry, kPrefThemeId); - Preferences::RegisterCallback(&ReflectSkeletonUIPrefToRegistry, - kPrefDrawTabsInTitlebar); + Preferences::RegisterCallback( + &ReflectSkeletonUIPrefToRegistry, + nsDependentCString(StaticPrefs::GetPrefName_browser_tabs_drawInTitlebar())); } # if defined(MOZ_LAUNCHER_PROCESS) diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h index 397a8fd86b9bb..ba9353406359b 100644 --- a/widget/LookAndFeel.h +++ b/widget/LookAndFeel.h @@ -246,12 +246,6 @@ class LookAndFeel { */ GTKCSDAvailable, - /* - * A boolean value indicating whether GTK+ system titlebar should be - * disabled by default. - */ - GTKCSDHideTitlebarByDefault, - /* * A boolean value indicating whether client-side decorations should * contain a minimize button. @@ -509,6 +503,11 @@ class LookAndFeel { */ static bool GetEchoPassword(); + /** + * Whether we should be drawing in the titlebar by default. + */ + static bool DrawInTitlebar(); + /** * The millisecond to mask password value. * This value is only valid when GetEchoPassword() returns true. diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index 7cab3abbfb15f..8ccee16dea834 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -789,10 +789,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { EnsureInit(); aResult = mCSDAvailable; break; - case IntID::GTKCSDHideTitlebarByDefault: - EnsureInit(); - aResult = mCSDHideTitlebarByDefault; - break; case IntID::GTKCSDMaximizeButton: EnsureInit(); aResult = mCSDMaximizeButton; @@ -1246,7 +1242,6 @@ void nsLookAndFeel::EnsureInit() { mCSDAvailable = nsWindow::GtkWindowDecoration() != nsWindow::GTK_DECORATION_NONE; - mCSDHideTitlebarByDefault = nsWindow::HideTitlebarByDefault(); mSystemTheme.Init(); @@ -1840,6 +1835,29 @@ char16_t nsLookAndFeel::GetPasswordCharacterImpl() { bool nsLookAndFeel::GetEchoPasswordImpl() { return false; } +bool nsLookAndFeel::GetDefaultDrawInTitlebar() { + static bool drawInTitlebar = []() { + // When user defined widget.default-hidden-titlebar don't do any + // heuristics and just follow it. + if (Preferences::HasUserValue("widget.default-hidden-titlebar")) { + return Preferences::GetBool("widget.default-hidden-titlebar", false); + } + + // Don't hide titlebar when it's disabled on current desktop. + const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); + if (!currentDesktop || nsWindow::GetSystemGtkWindowDecoration() == + nsWindow::GTK_DECORATION_NONE) { + return false; + } + + // We hide system titlebar on Gnome/ElementaryOS without any restriction. + return strstr(currentDesktop, "GNOME-Flashback:GNOME") || + strstr(currentDesktop, "GNOME") || + strstr(currentDesktop, "Pantheon"); + }(); + return drawInTitlebar; +} + void nsLookAndFeel::GetThemeInfo(nsACString& aInfo) { aInfo.Append(mSystemTheme.mName); aInfo.Append(" / "); diff --git a/widget/gtk/nsLookAndFeel.h b/widget/gtk/nsLookAndFeel.h index aa79710db9631..540313c106fda 100644 --- a/widget/gtk/nsLookAndFeel.h +++ b/widget/gtk/nsLookAndFeel.h @@ -32,6 +32,8 @@ class nsLookAndFeel final : public nsXPLookAndFeel { char16_t GetPasswordCharacterImpl() override; bool GetEchoPasswordImpl() override; + bool GetDefaultDrawInTitlebar() override; + template void WithAltThemeConfigured(const Callback&); @@ -155,7 +157,6 @@ class nsLookAndFeel final : public nsXPLookAndFeel { int32_t mCaretBlinkTime = 0; int32_t mCaretBlinkCount = -1; bool mCSDAvailable = false; - bool mCSDHideTitlebarByDefault = false; bool mCSDMaximizeButton = false; bool mCSDMinimizeButton = false; bool mCSDCloseButton = false; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index cfd56533c055a..cf48ca593d58c 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -8720,29 +8720,6 @@ bool nsWindow::TitlebarUseShapeMask() { return useShapeMask; } -bool nsWindow::HideTitlebarByDefault() { - static int hideTitlebar = []() { - // When user defined widget.default-hidden-titlebar don't do any - // heuristics and just follow it. - if (Preferences::HasUserValue("widget.default-hidden-titlebar")) { - return Preferences::GetBool("widget.default-hidden-titlebar", false); - } - - // Don't hide titlebar when it's disabled on current desktop. - const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); - if (!currentDesktop || - GetSystemGtkWindowDecoration() == GTK_DECORATION_NONE) { - return false; - } - - // We hide system titlebar on Gnome/ElementaryOS without any restriction. - return ((strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr || - strstr(currentDesktop, "GNOME") != nullptr || - strstr(currentDesktop, "Pantheon") != nullptr)); - }(); - return hideTitlebar; -} - int32_t nsWindow::RoundsWidgetCoordinatesTo() { return GdkCeiledScaleFactor(); } void nsWindow::GetCompositorWidgetInitData( diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index da321a7be649c..02901cb48703b 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -367,7 +367,6 @@ class nsWindow final : public nsBaseWidget { */ static GtkWindowDecoration GetSystemGtkWindowDecoration(); - static bool HideTitlebarByDefault(); static bool GetTopLevelWindowActiveState(nsIFrame* aFrame); static bool TitlebarUseShapeMask(); bool IsRemoteContent() { return HasRemoteContent(); } diff --git a/widget/headless/HeadlessLookAndFeelGTK.cpp b/widget/headless/HeadlessLookAndFeelGTK.cpp index 067f0750ac329..d072a4fb87095 100644 --- a/widget/headless/HeadlessLookAndFeelGTK.cpp +++ b/widget/headless/HeadlessLookAndFeelGTK.cpp @@ -256,7 +256,6 @@ nsresult HeadlessLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = 1; break; case IntID::GTKCSDAvailable: - case IntID::GTKCSDHideTitlebarByDefault: aResult = 0; break; case IntID::GTKCSDMinimizeButton: diff --git a/widget/nsXPLookAndFeel.cpp b/widget/nsXPLookAndFeel.cpp index 62eed79b9bd76..5f6799809304e 100644 --- a/widget/nsXPLookAndFeel.cpp +++ b/widget/nsXPLookAndFeel.cpp @@ -160,7 +160,6 @@ static const char sIntPrefs[][43] = { "ui.contextMenuOffsetVertical", "ui.contextMenuOffsetHorizontal", "ui.GtkCSDAvailable", - "ui.GtkCSDHideTitlebarByDefault", "ui.GtkCSDMinimizeButton", "ui.GtkCSDMaximizeButton", "ui.GtkCSDCloseButton", @@ -1266,6 +1265,18 @@ uint32_t LookAndFeel::GetPasswordMaskDelay() { return delay; } +bool LookAndFeel::DrawInTitlebar() { + switch (StaticPrefs::browser_tabs_drawInTitlebar()) { + case 0: + return false; + case 1: + return true; + default: + break; + } + return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar(); +} + void LookAndFeel::GetThemeInfo(nsACString& aOut) { nsLookAndFeel::GetInstance()->GetThemeInfo(aOut); } diff --git a/widget/nsXPLookAndFeel.h b/widget/nsXPLookAndFeel.h index 05e75f709906b..3218c9c7bd025 100644 --- a/widget/nsXPLookAndFeel.h +++ b/widget/nsXPLookAndFeel.h @@ -56,6 +56,8 @@ class nsXPLookAndFeel : public mozilla::LookAndFeel { virtual uint32_t GetPasswordMaskDelayImpl() { return 600; } + virtual bool GetDefaultDrawInTitlebar() { return true; } + static bool LookAndFeelFontToStyle(const LookAndFeelFont&, nsString& aName, gfxFontStyle&); static LookAndFeelFont StyleToLookAndFeelFont(const nsAString& aName, diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py index 7aa15cfc09b00..22244864d4ea0 100644 --- a/xpcom/ds/StaticAtoms.py +++ b/xpcom/ds/StaticAtoms.py @@ -2225,7 +2225,6 @@ Atom("_moz_is_resource_document", "-moz-is-resource-document"), Atom("_moz_swipe_animation_enabled", "-moz-swipe-animation-enabled"), Atom("_moz_gtk_csd_available", "-moz-gtk-csd-available"), - Atom("_moz_gtk_csd_hide_titlebar_by_default", "-moz-gtk-csd-hide-titlebar-by-default"), Atom("_moz_gtk_csd_titlebar_radius", "-moz-gtk-csd-titlebar-radius"), Atom("_moz_gtk_csd_minimize_button", "-moz-gtk-csd-minimize-button"), Atom("_moz_gtk_csd_maximize_button", "-moz-gtk-csd-maximize-button"), diff --git a/xpcom/system/nsIXULRuntime.idl b/xpcom/system/nsIXULRuntime.idl index 8855ff0d8c0d2..e0b88396d6523 100644 --- a/xpcom/system/nsIXULRuntime.idl +++ b/xpcom/system/nsIXULRuntime.idl @@ -319,6 +319,9 @@ interface nsIXULRuntime : nsISupports /** Whether the chrome color-scheme is dark */ readonly attribute boolean chromeColorSchemeIsDark; + /** Whether we should draw over the titlebar */ + readonly attribute boolean drawInTitlebar; + /** * The path of the shortcut used to start the current process, or "" if none. * From 0ae349efa76e31b96b778b1f5942d112c0bb77d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 21 Oct 2021 09:07:13 +0000 Subject: [PATCH 09/35] Bug 1736518 - Fix CSD check in nsLookAndFeel. r=stransky This is a regression from bug 1681356. nsWindow::GtkWindowDecoration() default-initializes a GtkWindowDecoration, which ends up being GTK_DECORATION_SYSTEM since that's the first variant. Fix the check by using the proper function GetSystemGtkWindowDecoration(). Quite a footgun... Differential Revision: https://phabricator.services.mozilla.com/D129005 --- widget/gtk/nsLookAndFeel.cpp | 14 +++++++------- widget/gtk/nsLookAndFeel.h | 3 --- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index 8ccee16dea834..205bef4ef96a6 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -77,6 +77,8 @@ static void settings_changed_cb(GtkSettings*, GParamSpec*, void*) { widget::IMContextWrapper::OnThemeChanged(); } +static bool sCSDAvailable; + nsLookAndFeel::nsLookAndFeel() { static constexpr nsLiteralCString kObservedSettings[] = { // Affects system font sizes. @@ -110,6 +112,9 @@ nsLookAndFeel::nsLookAndFeel() { g_signal_connect_after(settings, setting.get(), G_CALLBACK(settings_changed_cb), nullptr); } + + sCSDAvailable = + nsWindow::GetSystemGtkWindowDecoration() != nsWindow::GTK_DECORATION_NONE; } nsLookAndFeel::~nsLookAndFeel() { @@ -786,8 +791,7 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = 2; break; case IntID::GTKCSDAvailable: - EnsureInit(); - aResult = mCSDAvailable; + aResult = sCSDAvailable; break; case IntID::GTKCSDMaximizeButton: EnsureInit(); @@ -1240,9 +1244,6 @@ void nsLookAndFeel::EnsureInit() { mCaretBlinkCount = -1; } - mCSDAvailable = - nsWindow::GtkWindowDecoration() != nsWindow::GTK_DECORATION_NONE; - mSystemTheme.Init(); mCSDCloseButton = false; @@ -1845,8 +1846,7 @@ bool nsLookAndFeel::GetDefaultDrawInTitlebar() { // Don't hide titlebar when it's disabled on current desktop. const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); - if (!currentDesktop || nsWindow::GetSystemGtkWindowDecoration() == - nsWindow::GTK_DECORATION_NONE) { + if (!currentDesktop || !sCSDAvailable) { return false; } diff --git a/widget/gtk/nsLookAndFeel.h b/widget/gtk/nsLookAndFeel.h index 540313c106fda..828930a1bc2a2 100644 --- a/widget/gtk/nsLookAndFeel.h +++ b/widget/gtk/nsLookAndFeel.h @@ -44,8 +44,6 @@ class nsLookAndFeel final : public nsXPLookAndFeel { static void ConfigureTheme(const LookAndFeelTheme& aTheme); - bool IsCSDAvailable() const { return mCSDAvailable; } - static const nscolor kBlack = NS_RGB(0, 0, 0); static const nscolor kWhite = NS_RGB(255, 255, 255); @@ -156,7 +154,6 @@ class nsLookAndFeel final : public nsXPLookAndFeel { int32_t mCaretBlinkTime = 0; int32_t mCaretBlinkCount = -1; - bool mCSDAvailable = false; bool mCSDMaximizeButton = false; bool mCSDMinimizeButton = false; bool mCSDCloseButton = false; From 84fd4bcb5ff12ac24eef8214cfc7d66e25e3dc48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 21 Oct 2021 09:07:14 +0000 Subject: [PATCH 10/35] Bug 1736518 - Remove GTK_CSD_DECORATION_UNKNOWN. r=stransky Clean up the check while at it. Differential Revision: https://phabricator.services.mozilla.com/D129006 --- widget/gtk/nsWindow.cpp | 128 +++++++++++++++++----------------------- widget/gtk/nsWindow.h | 1 - 2 files changed, 53 insertions(+), 76 deletions(-) diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index cf48ca593d58c..8c3b6bd423265 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -271,8 +271,6 @@ static SystemTimeConverter& TimeConverter() { return sTimeConverterSingleton; } -nsWindow::GtkWindowDecoration nsWindow::sGtkWindowDecoration = - GTK_DECORATION_UNKNOWN; bool nsWindow::sTransparentMainWindow = false; namespace mozilla { @@ -8616,83 +8614,63 @@ nsresult nsWindow::SynthesizeNativeTouchPadPinch( } nsWindow::GtkWindowDecoration nsWindow::GetSystemGtkWindowDecoration() { - if (sGtkWindowDecoration != GTK_DECORATION_UNKNOWN) { - return sGtkWindowDecoration; - } + static GtkWindowDecoration sGtkWindowDecoration = [] { + // Allow MOZ_GTK_TITLEBAR_DECORATION to override our heuristics + if (const char* decorationOverride = + getenv("MOZ_GTK_TITLEBAR_DECORATION")) { + if (strcmp(decorationOverride, "none") == 0) { + return GTK_DECORATION_NONE; + } + if (strcmp(decorationOverride, "client") == 0) { + return GTK_DECORATION_CLIENT; + } + if (strcmp(decorationOverride, "system") == 0) { + return GTK_DECORATION_SYSTEM; + } + } - // Allow MOZ_GTK_TITLEBAR_DECORATION to override our heuristics - const char* decorationOverride = getenv("MOZ_GTK_TITLEBAR_DECORATION"); - if (decorationOverride) { - if (strcmp(decorationOverride, "none") == 0) { - sGtkWindowDecoration = GTK_DECORATION_NONE; - } else if (strcmp(decorationOverride, "client") == 0) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - } else if (strcmp(decorationOverride, "system") == 0) { - sGtkWindowDecoration = GTK_DECORATION_SYSTEM; + // nsWindow::GetSystemGtkWindowDecoration can be called from various + // threads so we can't use gfxPlatformGtk here. + if (GdkIsWaylandDisplay()) { + return GTK_DECORATION_CLIENT; } - return sGtkWindowDecoration; - } - // nsWindow::GetSystemGtkWindowDecoration can be called from various threads - // so we can't use gfxPlatformGtk here. - if (GdkIsWaylandDisplay()) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - return sGtkWindowDecoration; - } - - // GTK_CSD forces CSD mode - use also CSD because window manager - // decorations does not work with CSD. - // We check GTK_CSD as well as gtk_window_should_use_csd() does. - const char* csdOverride = getenv("GTK_CSD"); - if (csdOverride && *csdOverride == '1') { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - return sGtkWindowDecoration; - } - - const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); - if (currentDesktop) { - // GNOME Flashback (fallback) - if (strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_SYSTEM; - // Pop Linux Bug 1629198 - } else if (strstr(currentDesktop, "pop:GNOME") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - // gnome-shell - } else if (strstr(currentDesktop, "GNOME") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_SYSTEM; - } else if (strstr(currentDesktop, "XFCE") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - } else if (strstr(currentDesktop, "X-Cinnamon") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_SYSTEM; - // KDE Plasma - } else if (strstr(currentDesktop, "KDE") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - } else if (strstr(currentDesktop, "Enlightenment") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - } else if (strstr(currentDesktop, "LXDE") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - } else if (strstr(currentDesktop, "openbox") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - } else if (strstr(currentDesktop, "i3") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_NONE; - } else if (strstr(currentDesktop, "MATE") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - // Ubuntu Unity - } else if (strstr(currentDesktop, "Unity") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_SYSTEM; - // Elementary OS - } else if (strstr(currentDesktop, "Pantheon") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - } else if (strstr(currentDesktop, "LXQt") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_SYSTEM; - } else if (strstr(currentDesktop, "Deepin") != nullptr) { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; - } else { - sGtkWindowDecoration = GTK_DECORATION_CLIENT; + // GTK_CSD forces CSD mode - use also CSD because window manager + // decorations does not work with CSD. + // We check GTK_CSD as well as gtk_window_should_use_csd() does. + const char* csdOverride = getenv("GTK_CSD"); + if (csdOverride && *csdOverride == '1') { + return GTK_DECORATION_CLIENT; } - } else { - sGtkWindowDecoration = GTK_DECORATION_NONE; - } + + const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); + if (!currentDesktop) { + return GTK_DECORATION_NONE; + } + // clang-format off + if (strstr(currentDesktop, "pop:GNOME") || // Bug 1629198 + strstr(currentDesktop, "KDE") || + strstr(currentDesktop, "Enlightenment") || + strstr(currentDesktop, "LXDE") || + strstr(currentDesktop, "openbox") || + strstr(currentDesktop, "MATE") || + strstr(currentDesktop, "Pantheon") || + strstr(currentDesktop, "Deepin")) { + return GTK_DECORATION_CLIENT; + } + if (strstr(currentDesktop, "GNOME") || + strstr(currentDesktop, "X-Cinnamon") || + strstr(currentDesktop, "LXQt") || + strstr(currentDesktop, "Unity")) { + return GTK_DECORATION_SYSTEM; + } + if (strstr(currentDesktop, "i3")) { + return GTK_DECORATION_NONE; + } + // clang-format on + + return GTK_DECORATION_CLIENT; + }(); return sGtkWindowDecoration; } diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index 02901cb48703b..c4bd2502e0b7e 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -359,7 +359,6 @@ class nsWindow final : public nsBaseWidget { GTK_DECORATION_SYSTEM, // CSD including shadows GTK_DECORATION_CLIENT, // CSD without shadows GTK_DECORATION_NONE, // WM does not support CSD at all - GTK_DECORATION_UNKNOWN } GtkWindowDecoration; /** * Get the support of Client Side Decoration by checking From b26f16d4bc94359c832cb13a4c8cbca651269f92 Mon Sep 17 00:00:00 2001 From: Frederik Braun Date: Thu, 21 Oct 2021 09:22:59 +0000 Subject: [PATCH 11/35] Bug 1736983 - add UseCounter for Sanitizer API methods r=emilio Differential Revision: https://phabricator.services.mozilla.com/D129131 --- dom/base/UseCounters.conf | 5 +++++ dom/webidl/Element.webidl | 2 +- dom/webidl/Sanitizer.webidl | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dom/base/UseCounters.conf b/dom/base/UseCounters.conf index 34a16b52a39ea..ec4ca79fe856d 100644 --- a/dom/base/UseCounters.conf +++ b/dom/base/UseCounters.conf @@ -404,3 +404,8 @@ custom feTile uses the feTile SVG filter. custom feTurbulence uses the feTurbulence SVG filter. custom WrFilterFallback triggers the blob fallback for an SVG filter. + +// Sanitizer API +method Sanitizer.sanitize +method Sanitizer.sanitizeFor +method Element.setHTML \ No newline at end of file diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl index 72698f447d47a..61fc2e8cde6fe 100644 --- a/dom/webidl/Element.webidl +++ b/dom/webidl/Element.webidl @@ -388,6 +388,6 @@ dictionary SetHTMLOptions { }; partial interface Element { - [Throws, Pref="dom.security.sanitizer.enabled"] + [UseCounter, Throws, Pref="dom.security.sanitizer.enabled"] void setHTML(DOMString aInnerHTML, optional SetHTMLOptions options = {}); }; diff --git a/dom/webidl/Sanitizer.webidl b/dom/webidl/Sanitizer.webidl index 9fcedf3fb672e..68e7728681192 100644 --- a/dom/webidl/Sanitizer.webidl +++ b/dom/webidl/Sanitizer.webidl @@ -18,9 +18,9 @@ typedef record> AttributeMatchList; interface Sanitizer { [Throws] constructor(optional SanitizerConfig sanitizerConfig = {}); - [Throws] + [UseCounter, Throws] DocumentFragment sanitize(SanitizerInput input); - [Throws] + [UseCounter, Throws] Element? sanitizeFor(DOMString element, DOMString input); }; From d551f24481e7a921442dee3619421146b9b7a62a Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Thu, 21 Oct 2021 09:48:06 +0000 Subject: [PATCH 12/35] Bug 1694085 - Serialize and deserialize the codec extra-data when sending an AudioInfo to the RDD. r=bryce Differential Revision: https://phabricator.services.mozilla.com/D128895 --- dom/media/ipc/MediaIPCUtils.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/media/ipc/MediaIPCUtils.h b/dom/media/ipc/MediaIPCUtils.h index b940a3af8ba47..ed9f9006b88cd 100644 --- a/dom/media/ipc/MediaIPCUtils.h +++ b/dom/media/ipc/MediaIPCUtils.h @@ -95,6 +95,7 @@ struct ParamTraits { WriteParam(aMsg, aParam.mProfile); WriteParam(aMsg, aParam.mExtendedProfile); WriteParam(aMsg, *aParam.mCodecSpecificConfig); + WriteParam(aMsg, *aParam.mExtraData); } static bool Read(const Message* aMsg, PickleIterator* aIter, @@ -106,7 +107,8 @@ struct ParamTraits { ReadParam(aMsg, aIter, &aResult->mBitDepth) && ReadParam(aMsg, aIter, &aResult->mProfile) && ReadParam(aMsg, aIter, &aResult->mExtendedProfile) && - ReadParam(aMsg, aIter, aResult->mCodecSpecificConfig.get())) { + ReadParam(aMsg, aIter, aResult->mCodecSpecificConfig.get()) && + ReadParam(aMsg, aIter, aResult->mExtraData.get())) { return true; } return false; From ade076bf6e460a79eeb019875767330f576bbd86 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Thu, 21 Oct 2021 09:48:07 +0000 Subject: [PATCH 13/35] Bug 1694085 - Add a test that verifies that HE-AAC plays back correctly including higher frequency content both with MSE and HTTP playback. r=bryce Depends on D128895 Differential Revision: https://phabricator.services.mozilla.com/D128907 --- dom/media/mediasource/test/mochitest.ini | 2 + .../test/test_HEAAC_extradata.html | 89 ++++++++++++++++++ .../mediasource/test/whitenoise-he-aac-5s.mp4 | Bin 0 -> 27078 bytes 3 files changed, 91 insertions(+) create mode 100644 dom/media/mediasource/test/test_HEAAC_extradata.html create mode 100644 dom/media/mediasource/test/whitenoise-he-aac-5s.mp4 diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index ed43ac8376a2c..7549c3c7d9719 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -55,6 +55,7 @@ support-files = wmf_mismatchedaudiotime.mp4 bug1718709_low_res.mp4 bug1718709_high_res.mp4 + whitenoise-he-aac-5s.mp4 [test_AbortAfterPartialMediaSegment.html] [test_AppendPartialInitSegment.html] @@ -85,6 +86,7 @@ skip-if = os == 'win' # bug 1487973, (os == 'mac') # mac due to bug 1487973 [test_HaveMetadataUnbufferedSeek.html] [test_HaveMetadataUnbufferedSeek_mp4.html] +[test_HEAAC_extradata.html] [test_LiveSeekable.html] [test_LoadedDataFired_mp4.html] [test_LoadedMetadataFired.html] diff --git a/dom/media/mediasource/test/test_HEAAC_extradata.html b/dom/media/mediasource/test/test_HEAAC_extradata.html new file mode 100644 index 0000000000000..48cda9fe7d365 --- /dev/null +++ b/dom/media/mediasource/test/test_HEAAC_extradata.html @@ -0,0 +1,89 @@ + + + + HE-AAC decoding test + + + + + +

+
+
+ + diff --git a/dom/media/mediasource/test/whitenoise-he-aac-5s.mp4 b/dom/media/mediasource/test/whitenoise-he-aac-5s.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..db648b82294f4b257af22566f77c031174c7dc77 GIT binary patch literal 27078 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 H56%AptX&|a literal 0 HcmV?d00001 From 79e4b593436e6359b2bbaeca6244730091fe6553 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 21 Oct 2021 10:21:22 +0000 Subject: [PATCH 14/35] Bug 1736824 - [devtools] Fix "consoleFront is null" exception when reloading the page. r=nchevobbe Differential Revision: https://phabricator.services.mozilla.com/D129055 --- devtools/client/framework/toolbox.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js index e089f43c9a3a4..d9ddb4447fd9f 100644 --- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -720,7 +720,13 @@ Toolbox.prototype = { _onTargetDestroyed({ targetFront }) { if (targetFront.isTopLevel) { const consoleFront = targetFront.getCachedFront("console"); - consoleFront.off("inspectObject", this._onInspectObject); + // If the target has already been destroyed, its console front will + // also already be destroyed and so we won't be able to retrieve it. + // Nor is it important to clear its listener as fronts automatically clears + // all their listeners on destroy. + if (consoleFront) { + consoleFront.off("inspectObject", this._onInspectObject); + } targetFront.off("frame-update", this._updateFrames); } else if (this.selection) { this.selection.onTargetDestroyed(targetFront); From 31b127d6d3dd8d9ef328238d17bdaf0a9843383e Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 21 Oct 2021 10:21:22 +0000 Subject: [PATCH 15/35] Bug 1736824 - [devtools] Really stop listener and emitting events in DocumentEventsListener after being destroyed. r=nchevobbe We weren't removing listeners on the target actor, and also some late DOMContentLoaded/load event may be fired. We were getting exception because of late events when closing the toolbox on a still-loading page. Differential Revision: https://phabricator.services.mozilla.com/D129056 --- .../webconsole/listeners/document-events.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/devtools/server/actors/webconsole/listeners/document-events.js b/devtools/server/actors/webconsole/listeners/document-events.js index 8e57d530abfa3..d47e7d398aa1e 100644 --- a/devtools/server/actors/webconsole/listeners/document-events.js +++ b/devtools/server/actors/webconsole/listeners/document-events.js @@ -55,10 +55,10 @@ exports.DocumentEventsListener = DocumentEventsListener; DocumentEventsListener.prototype = { listen() { // Listen to will-navigate and do not emit a fake one as we only care about upcoming navigation - EventEmitter.on(this.targetActor, "will-navigate", this.onWillNavigate); + this.targetActor.on("will-navigate", this.onWillNavigate); // Listen to window-ready and then fake one in order to notify about dom-loading for the existing document - EventEmitter.on(this.targetActor, "window-ready", this.onWindowReady); + this.targetActor.on("window-ready", this.onWindowReady); // If the target actor isn't attached yet, attach it so that it starts emitting window-ready event // Only do that if this isn't a JSWindowActor based target as this won't emit window-ready anyway. if ( @@ -132,6 +132,9 @@ DocumentEventsListener.prototype = { }, onContentLoaded(event, isFrameSwitching) { + if (this.destroyed) { + return; + } // milliseconds since the UNIX epoch, when the parser finished its work // on the main document, that is when its Document.readyState changes to // 'interactive' and the corresponding readystatechange event is thrown @@ -141,6 +144,9 @@ DocumentEventsListener.prototype = { }, onLoad(event, isFrameSwitching) { + if (this.destroyed) { + return; + } // milliseconds since the UNIX epoch, when the parser finished its work // on the main document, that is when its Document.readyState changes to // 'complete' and the corresponding readystatechange event is thrown @@ -180,6 +186,9 @@ DocumentEventsListener.prototype = { }, destroy() { - this.listener = null; + // Also use a flag to silent onContentLoad and onLoad events + this.destroyed = true; + this.targetActor.off("will-navigate", this.onWillNavigate); + this.targetActor.off("window-ready", this.onWindowReady); }, }; From 70d1f984453da42aa8eed326fae9e05d5d7f59bf Mon Sep 17 00:00:00 2001 From: Sam Foster Date: Thu, 21 Oct 2021 10:37:16 +0000 Subject: [PATCH 16/35] Bug 1736749 - ensure we alwaysAskBeforeHandling downloads in browser_download_open_with_internal_handler.js to verify unknown-content-type dialog behavior when download improvements are flipped on. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D129095 --- ...ser_download_open_with_internal_handler.js | 95 ++++++++++++++++--- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/uriloader/exthandler/tests/mochitest/browser_download_open_with_internal_handler.js b/uriloader/exthandler/tests/mochitest/browser_download_open_with_internal_handler.js index 7709a1ea419c2..c32d4f17ddd3f 100644 --- a/uriloader/exthandler/tests/mochitest/browser_download_open_with_internal_handler.js +++ b/uriloader/exthandler/tests/mochitest/browser_download_open_with_internal_handler.js @@ -15,6 +15,11 @@ const TEST_PATH = getRootDirectory(gTestPath).replace( "https://example.com" ); +const MimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); +const HandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService( + Ci.nsIHandlerService +); + function waitForAcceptButtonToGetEnabled(doc) { let dialog = doc.querySelector("#unknownContentType"); let button = dialog.getButton("accept"); @@ -42,6 +47,25 @@ async function waitForPdfJS(browser, url) { return loadPromise; } +/** + * This test covers which choices are presented for downloaded files and how + * those choices are handled. When the download improvements are enabled + * (browser.download.improvements_to_download_panel pref) the unknown content + * dialog will be skipped altogether by default when downloading. + * To retain coverage for the non-default scenario, each task sets `alwaysAskBeforeHandling` + * to true for the relevant mime-type and extensions. + */ +function alwaysAskForHandlingTypes(typeExtensions) { + let mimeInfos = []; + for (let [type, ext] of Object.entries(typeExtensions)) { + const mimeInfo = MimeSvc.getFromTypeAndExtension(type, ext); + mimeInfo.alwaysAskBeforeHandling = true; + HandlerSvc.store(mimeInfo); + mimeInfos.push(mimeInfo); + } + return mimeInfos; +} + add_task(async function setup() { // Remove the security delay for the dialog during the test. await SpecialPowers.pushPrefEnv({ @@ -53,18 +77,15 @@ add_task(async function setup() { }); // Restore handlers after the whole test has run - const mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); - const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService( - Ci.nsIHandlerService - ); const registerRestoreHandler = function(type, ext) { - const mimeInfo = mimeSvc.getFromTypeAndExtension(type, ext); - const existed = handlerSvc.exists(mimeInfo); + const mimeInfo = MimeSvc.getFromTypeAndExtension(type, ext); + const existed = HandlerSvc.exists(mimeInfo); + registerCleanupFunction(() => { if (existed) { - handlerSvc.store(mimeInfo); + HandlerSvc.store(mimeInfo); } else { - handlerSvc.remove(mimeInfo); + HandlerSvc.remove(mimeInfo); } }); }; @@ -80,6 +101,11 @@ add_task(async function setup() { * is clicked from pdf.js. */ add_task(async function test_check_open_with_internal_handler() { + const mimeInfosToRestore = alwaysAskForHandlingTypes({ + "application/pdf": "pdf", + "binary/octet-stream": "pdf", + }); + for (let file of [ "file_pdf_application_pdf.pdf", "file_pdf_binary_octet_stream.pdf", @@ -234,6 +260,9 @@ add_task(async function test_check_open_with_internal_handler() { } await publicList.removeFinished(); } + for (let mimeInfo of mimeInfosToRestore) { + HandlerSvc.remove(mimeInfo); + } }); /** @@ -241,6 +270,11 @@ add_task(async function test_check_open_with_internal_handler() { * open the PDF into pdf.js */ add_task(async function test_check_open_with_external_application() { + const mimeInfosToRestore = alwaysAskForHandlingTypes({ + "application/pdf": "pdf", + "binary/octet-stream": "pdf", + }); + for (let file of [ "file_pdf_application_pdf.pdf", "file_pdf_binary_octet_stream.pdf", @@ -305,6 +339,9 @@ add_task(async function test_check_open_with_external_application() { } await publicList.removeFinished(); } + for (let mimeInfo of mimeInfosToRestore) { + HandlerSvc.remove(mimeInfo); + } }); /** @@ -319,14 +356,14 @@ add_task(async function test_check_open_with_external_then_internal() { } // This test covers a bug that only occurs when the mimeInfo is set to Always Ask - const mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); - const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService( - Ci.nsIHandlerService + const mimeInfo = MimeSvc.getFromTypeAndExtension("application/pdf", "pdf"); + console.log( + "mimeInfo.preferredAction is currently:", + mimeInfo.preferredAction ); - const mimeInfo = mimeSvc.getFromTypeAndExtension("application/pdf", "pdf"); mimeInfo.preferredAction = mimeInfo.alwaysAsk; mimeInfo.alwaysAskBeforeHandling = true; - handlerSvc.store(mimeInfo); + HandlerSvc.store(mimeInfo); for (let [file, mimeType] of [ ["file_pdf_application_pdf.pdf", "application/pdf"], @@ -334,7 +371,7 @@ add_task(async function test_check_open_with_external_then_internal() { ["file_pdf_application_unknown.pdf", "application/unknown"], ]) { info("Testing with " + file); - let originalMimeInfo = mimeSvc.getFromTypeAndExtension(mimeType, "pdf"); + let originalMimeInfo = MimeSvc.getFromTypeAndExtension(mimeType, "pdf"); let publicList = await Downloads.getList(Downloads.PUBLIC); registerCleanupFunction(async () => { @@ -448,7 +485,7 @@ add_task(async function test_check_open_with_external_then_internal() { // Now trigger the dialog again and select the system // default option to reset the state for the next iteration of the test. // Reset the state for the next iteration of the test. - handlerSvc.store(originalMimeInfo); + HandlerSvc.store(originalMimeInfo); DownloadIntegration.launchFile = oldLaunchFile; let [download] = await publicList.getAll(); if (download?.target.exists) { @@ -469,6 +506,11 @@ add_task(async function test_check_open_with_external_then_internal() { */ add_task( async function test_internal_handler_hidden_with_viewable_internally_type() { + const mimeInfosToRestore = alwaysAskForHandlingTypes({ + "text/xml": "xml", + "binary/octet-stream": "xml", + }); + for (let [file, checkDefault] of [ // The default for binary/octet-stream is changed by the PDF tests above, // this may change given bug 1659008, so I'm just ignoring the default for now. @@ -502,6 +544,9 @@ add_task( dialog.cancelDialog(); BrowserTestUtils.removeTab(loadingTab); } + for (let mimeInfo of mimeInfosToRestore) { + HandlerSvc.remove(mimeInfo); + } } ); @@ -510,6 +555,10 @@ add_task( * for non-PDF, non-viewable-internally types. */ add_task(async function test_internal_handler_hidden_with_other_type() { + const mimeInfosToRestore = alwaysAskForHandlingTypes({ + "text/plain": "txt", + }); + let dialogWindowPromise = BrowserTestUtils.domWindowOpenedAndLoaded(); let loadingTab = await BrowserTestUtils.openNewForegroundTab( gBrowser, @@ -536,6 +585,9 @@ add_task(async function test_internal_handler_hidden_with_other_type() { let dialog = doc.querySelector("#unknownContentType"); dialog.cancelDialog(); BrowserTestUtils.removeTab(loadingTab); + for (let mimeInfo of mimeInfosToRestore) { + HandlerSvc.remove(mimeInfo); + } }); /** @@ -543,6 +595,10 @@ add_task(async function test_internal_handler_hidden_with_other_type() { * when the feature is disabled for PDFs. */ add_task(async function test_internal_handler_hidden_with_pdf_pref_disabled() { + const mimeInfosToRestore = alwaysAskForHandlingTypes({ + "application/pdf": "pdf", + "binary/octet-stream": "pdf", + }); await SpecialPowers.pushPrefEnv({ set: [["browser.helperApps.showOpenOptionForPdfJS", false]], }); @@ -575,6 +631,9 @@ add_task(async function test_internal_handler_hidden_with_pdf_pref_disabled() { dialog.cancelDialog(); BrowserTestUtils.removeTab(loadingTab); } + for (let mimeInfo of mimeInfosToRestore) { + HandlerSvc.remove(mimeInfo); + } }); /** @@ -583,6 +642,9 @@ add_task(async function test_internal_handler_hidden_with_pdf_pref_disabled() { */ add_task( async function test_internal_handler_hidden_with_viewable_internally_pref_disabled() { + const mimeInfosToRestore = alwaysAskForHandlingTypes({ + "text/xml": "xml", + }); await SpecialPowers.pushPrefEnv({ set: [["browser.helperApps.showOpenOptionForViewableInternally", false]], }); @@ -610,5 +672,8 @@ add_task( let dialog = doc.querySelector("#unknownContentType"); dialog.cancelDialog(); BrowserTestUtils.removeTab(loadingTab); + for (let mimeInfo of mimeInfosToRestore) { + HandlerSvc.remove(mimeInfo); + } } ); From 858c0957f78a2c535c8e715f42539ee70813a48f Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Thu, 21 Oct 2021 11:02:09 +0000 Subject: [PATCH 17/35] Bug 1160000 - Add xpcshell-tests for NTLM authentication r=necko-reviewers,dragana Differential Revision: https://phabricator.services.mozilla.com/D129007 --- .../test/unit/test_ntlm_proxy_and_web_auth.js | 379 ++++++++++++++++++ netwerk/test/unit/test_ntlm_proxy_auth.js | 373 +++++++++++++++++ netwerk/test/unit/test_ntlm_web_auth.js | 261 ++++++++++++ netwerk/test/unit/xpcshell.ini | 3 + 4 files changed, 1016 insertions(+) create mode 100644 netwerk/test/unit/test_ntlm_proxy_and_web_auth.js create mode 100644 netwerk/test/unit/test_ntlm_proxy_auth.js create mode 100644 netwerk/test/unit/test_ntlm_web_auth.js diff --git a/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js b/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js new file mode 100644 index 0000000000000..5311e5b407c0f --- /dev/null +++ b/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js @@ -0,0 +1,379 @@ +// Unit tests for a NTLM authenticated proxy, proxying for a NTLM authenticated +// web server. +// +// Currently the tests do not determine whether the Authentication dialogs have +// been displayed. +// +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + httpserver.identity.primaryPort; +}); + +const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2; +const nsIAuthInformation = Components.interfaces.nsIAuthInformation; + + +function AuthPrompt() { +} + +AuthPrompt.prototype = { + user: "guest", + pass: "guest", + + QueryInterface: function authprompt_qi(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIAuthPrompt2)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + promptAuth: function ap_promptAuth(channel, level, authInfo) { + authInfo.username = this.user; + authInfo.password = this.pass; + + return true; + }, + + asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + } +}; + +function Requestor() { +} + +Requestor.prototype = { + QueryInterface: function requestor_qi(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIInterfaceRequestor)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + getInterface: function requestor_gi(iid) { + if ( iid.equals(Components.interfaces.nsIAuthPrompt2)) { + // Allow the prompt to store state by caching it here + if (!this.prompt) + this.prompt = new AuthPrompt(); + return this.prompt; + } + + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + prompt: null +}; + +function makeChan(url, loadingUrl) { + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + var principal = ssm.createCodebasePrincipal(ios.newURI(loadingUrl, null, null), {}); + return NetUtil.newChannel( + { uri: url + , loadingPrincipal: principal + , securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL + , contentPolicyType: Components.interfaces.nsIContentPolicy.TYPE_OTHER + }); +} + +function TestListener() { +} +TestListener.prototype.onStartRequest = function(request, context) { + + // Need to do the instanceof to allow request.responseStatus + // to be read. + if (!(request instanceof Components.interfaces.nsIHttpChannel)) + do_print("Expecting an HTTP channel"); + + Assert.equal( expectedResponse, request.responseStatus,"HTTP Status code") ; +} +TestListener.prototype.onStopRequest = function(request, context, status) { + + Assert.equal(expectedRequests, requestsMade, "Number of requests made "); + + if (current_test < (tests.length - 1)) { + current_test++; + tests[current_test](); + } else { + do_test_pending(); + httpserver.stop(do_test_finished); + } + + do_test_finished(); + +} +TestListener.prototype.onDataAvaiable = function(request, context, stream, offset, count) { + var data = read_stream(stream, count); +} + +// NTLM Messages, for the received type 1 and 3 messages only check that they +// are of the expected type. +const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; +const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; +const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; +const NTLM_PREFIX_LEN = 21; + +const NTLM_CHALLENGE = NTLM_TYPE2_PREFIX + + "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" + + "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" + + "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" + + "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="; + +const PROXY_CHALLENGE = NTLM_TYPE2_PREFIX + + "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" + + "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" + + "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" + + "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA"; + + + +// Proxy and Web server responses for the happy path scenario. +// i.e. successful proxy auth and successful web server auth +// +function authHandler( metadata, response) { + switch (requestsMade) { + case 0: + // Proxy - First request to the Proxy resppond with a 407 to start auth + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + case 1: + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); + break; + case 2: + // Proxy - Expecting a type 3 Authenticate message from the client + // Will respond with a 401 to start web server auth sequence + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + response.setHeader( "WWW-Authenticate", "NTLM", false); + break; + case 3: + // Web Server - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + response.setHeader( "WWW-Authenticate", NTLM_CHALLENGE, false); + break; + case 4: + // Web Server - Expecting a type 3 Authenticate message from the client + var authorization = metadata.getHeader( "Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine( metadata.httpVersion, 200, "Successful"); + break; + default: + // We should be authenticated and further requests are permitted + var authorization = metadata.getHeader( "Authorization"); + var authorization = metadata.getHeader( "Proxy-Authorization"); + Assert.isnull( authorization); + response.setStatusLine( metadata.httpVersion, 200, "Successful"); + } + requestsMade++; +} + +// Proxy responses simulating an invalid proxy password +// Note: that the connection should not be reused after the +// proxy auth fails. +// +function authHandlerInvalidProxyPassword( metadata, response) { + switch (requestsMade) { + case 0: + // Proxy - First request respond with a 407 to initiate auth sequence + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + case 1: + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); + break; + case 2: + // Proxy - Expecting a type 3 Authenticate message from the client + // Respond with a 407 to indicate invalid credentials + // + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + default: + // Strictly speaking the connection should not be reused at this point + // and reaching here should be an error, but have commented out for now + //do_print( "ERROR: NTLM Proxy Authentication, connection should not be reused"); + //Assert.fail(); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + } + requestsMade++; +} + +// Proxy and web server responses simulating a successful Proxy auth +// and a failed web server auth +// Note: the connection should not be reused once the password failure is +// detected +function authHandlerInvalidWebPassword( metadata, response) { + switch (requestsMade) { + case 0: + // Proxy - First request return a 407 to start Proxy auth + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + case 1: + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", NTLM_CHALLENGE, false); + break; + case 2: + // Proxy - Expecting a type 3 Authenticate message from the client + // Responds with a 401 to start web server auth + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + response.setHeader( "WWW-Authenticate", "NTLM", false); + break; + case 3: + // Web Server - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + response.setHeader( "WWW-Authenticate", NTLM_CHALLENGE, false); + break; + case 4: + // Web Server - Expecting a type 3 Authenticate message from the client + // Respond with a 401 to restart the auth sequence. + var authorization = metadata.getHeader( "Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + break; + default: + // We should not get called past step 4 + do_print( "ERROR: NTLM Auth failed connection should not be reused"); + Assert.fail(); + } + requestsMade++; +} + +// Tests to run test_bad_proxy_pass and test_bad_web_pass are split into two stages +// so that once we determine how detect password dialog displays we can check +// that the are displayed correctly, i.e. proxy password should not be prompted +// for when retrying the web server password +var tests = [ + test_happy_path, + test_bad_proxy_pass_stage01, + test_bad_proxy_pass_stage02, + test_bad_web_pass_stage01, + test_bad_web_pass_stage02 +]; +var current_test = 0; + + +var httpserver = null; +function run_test() { + + httpserver = new HttpServer() + httpserver.start(-1); + + const prefs = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefBranch); + prefs.setCharPref("network.proxy.http", "localhost"); + prefs.setIntPref("network.proxy.http_port", httpserver.identity.primaryPort); + prefs.setCharPref("network.proxy.no_proxies_on", ""); + prefs.setIntPref("network.proxy.type", 1); + + tests[0](); +} + +var expectedRequests = 0; // Number of HTTP requests that are expected +var requestsMade = 0; // The number of requests that were made +var expectedResponse = 0; // The response code + // Note that any test failures in the HTTP handler + // will manifest as a 500 response code + +// Common test setup +// Parameters: +// path - path component of the URL +// handler - http handler function for the httpserver +// requests - expected number oh http requests +// response - expected http response code +// clearCache - clear the authentication cache before running the test +function setupTest( path, handler, requests, response, clearCache) { + + requestsMade = 0; + expectedRequests = requests; + expectedResponse = response; + + // clear the auth cache if requested + if (clearCache) { + do_print( "Clearing auth cache"); + Cc["@mozilla.org/network/http-auth-manager;1"] + .getService(Ci.nsIHttpAuthManager) + .clearAll(); + } + + var chan = makeChan( URL + path, URL); + httpserver.registerPathHandler( path, handler ); + chan.notificationCallbacks = new Requestor(); + chan.asyncOpen2(new TestListener()); + + return chan; +} + +// Happy code path +// Succesful proxy and web server auth. +function test_happy_path() { + do_print("RUNNING TEST: test_happy_path"); + var chan = setupTest( "/auth", authHandler, 5, 200, 1); + + do_test_pending(); +} + +// Failed proxy authentication +function test_bad_proxy_pass_stage01() { + do_print("RUNNING TEST: test_bad_proxy_pass_stage01"); + var chan = setupTest( "/auth", authHandlerInvalidProxyPassword, 4, 407, 1); + + do_test_pending(); +} +// Successful logon after failed proxy auth +function test_bad_proxy_pass_stage02() { + do_print("RUNNING TEST: test_bad_proxy_pass_stage02"); + var chan = setupTest( "/auth", authHandler, 5, 200, 0); + + do_test_pending(); +} + +// successful proxy logon, unsuccessful web server sign on +function test_bad_web_pass_stage01() { + do_print("RUNNING TEST: test_bad_web_pass_stage01"); + var chan = setupTest( "/auth", authHandlerInvalidWebPassword, 5, 401, 1); + + do_test_pending(); +} +// successful logon after failed web server auth. +function test_bad_web_pass_stage02() { + do_print("RUNNING TEST: test_bad_web_pass_stage02"); + var chan = setupTest( "/auth", authHandler, 5, 200, 0); + + do_test_pending(); +} diff --git a/netwerk/test/unit/test_ntlm_proxy_auth.js b/netwerk/test/unit/test_ntlm_proxy_auth.js new file mode 100644 index 0000000000000..c569ecff78b0e --- /dev/null +++ b/netwerk/test/unit/test_ntlm_proxy_auth.js @@ -0,0 +1,373 @@ +// Unit tests for a NTLM authenticated proxy +// +// Currently the tests do not determine whether the Authentication dialogs have +// been displayed. +// +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + httpserver.identity.primaryPort; +}); + +const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2; +const nsIAuthInformation = Components.interfaces.nsIAuthInformation; + + +function AuthPrompt() { +} + +AuthPrompt.prototype = { + user: "guest", + pass: "guest", + + QueryInterface: function authprompt_qi(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIAuthPrompt2)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + promptAuth: function ap_promptAuth(channel, level, authInfo) { + authInfo.username = this.user; + authInfo.password = this.pass; + + return true; + }, + + asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + } +}; + +function Requestor() { +} + +Requestor.prototype = { + QueryInterface: function requestor_qi(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIInterfaceRequestor)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + getInterface: function requestor_gi(iid) { + if ( iid.equals(Components.interfaces.nsIAuthPrompt2)) { + // Allow the prompt to store state by caching it here + if (!this.prompt) + this.prompt = new AuthPrompt(); + return this.prompt; + } + + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + prompt: null +}; + +function makeChan(url, loadingUrl) { + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + var principal = ssm.createCodebasePrincipal(ios.newURI(loadingUrl, null, null), {}); + return NetUtil.newChannel( + { uri: url + , loadingPrincipal: principal + , securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL + , contentPolicyType: Components.interfaces.nsIContentPolicy.TYPE_OTHER + }); +} + +function TestListener() { +} +TestListener.prototype.onStartRequest = function(request, context) { + + // Need to do the instanceof to allow request.responseStatus + // to be read. + if (!(request instanceof Components.interfaces.nsIHttpChannel)) + do_print("Expecting an HTTP channel"); + + Assert.equal( expectedResponse, request.responseStatus,"HTTP Status code") ; +} +TestListener.prototype.onStopRequest = function(request, context, status) { + + Assert.equal(expectedRequests, requestsMade, "Number of requests made "); + Assert.equal(exptTypeOneCount, ntlmTypeOneCount, "Number of type one messages received"); + Assert.equal(exptTypeTwoCount, ntlmTypeTwoCount, "Number of type two messages received"); + + if (current_test < (tests.length - 1)) { + current_test++; + tests[current_test](); + } else { + do_test_pending(); + httpserver.stop(do_test_finished); + } + + do_test_finished(); + +} +TestListener.prototype.onDataAvaiable = function(request, context, stream, offset, count) { + var data = read_stream(stream, count); +} + +// NTLM Messages, for the received type 1 and 3 messages only check that they +// are of the expected type. +const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; +const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; +const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; +const NTLM_PREFIX_LEN = 21; + +const PROXY_CHALLENGE = NTLM_TYPE2_PREFIX + + "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" + + "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" + + "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" + + "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA"; + + + +// Proxy responses for the happy path scenario. +// i.e. successful proxy auth +// +function successfulAuth( metadata, response) { + switch (requestsMade) { + case 0: + // Proxy - First request to the Proxy resppond with a 407 to start auth + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + case 1: + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); + break; + case 2: + // Proxy - Expecting a type 3 Authenticate message from the client + // Will respond with a 401 to start web server auth sequence + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine( metadata.httpVersion, 200, "Successful"); + break; + default: + // We should be authenticated and further requests are permitted + var authorization = metadata.getHeader( "Proxy-Authorization"); + Assert.isnull( authorization); + response.setStatusLine( metadata.httpVersion, 200, "Successful"); + } + requestsMade++; +} + +// Proxy responses simulating an invalid proxy password +// Note: that the connection should not be reused after the +// proxy auth fails. +// +function failedAuth( metadata, response) { + switch (requestsMade) { + case 0: + // Proxy - First request respond with a 407 to initiate auth sequence + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + case 1: + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); + break; + case 2: + // Proxy - Expecting a type 3 Authenticate message from the client + // Respond with a 407 to indicate invalid credentials + // + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + default: + // Strictly speaking the connection should not be reused at this point + // commenting out for now. + // do_print( "ERROR: NTLM Proxy Authentication, connection should not be reused"); + // assert.fail(); + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + } + requestsMade++; +} +// +// Simulate a connection reset once the connection has been authenticated +// Detects bug 486508 +// +function connectionReset( metadata, response) { + switch (requestsMade) { + case 0: + // Proxy - First request to the Proxy resppond with a 407 to start auth + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + case 1: + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + ntlmTypeOneCount++; + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); + break; + case 2: + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + ntlmTypeTwoCount++; + response.seizePower(); + response.bodyOutPutStream.close(); + response.finish(); + break; + default: + // Should not get any further requests on this channel + do_print( "ERROR: NTLM Proxy Authentication, connection should not be reused"); + assert.fail(); + } + requestsMade++; +} + +// +// Reset the connection after a nogotiate message has been received +// +function connectionReset02( metadata, response) { + switch (requestsMade) { + case 0: + // Proxy - First request to the Proxy resppond with a 407 to start auth + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", "NTLM", false); + break; + case 1: + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + ntlmTypeOneCount++; + response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); + response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); + response.finish(); + response.seizePower(); + response.bodyOutPutStream.close(); + break; + default: + // Should not get any further requests on this channel + do_print( "ERROR: NTLM Proxy Authentication, connection should not be reused"); + assert.fail(); + } + requestsMade++; +} +var tests = [ + test_happy_path, + test_failed_auth, + test_connection_reset, + test_connection_reset02 +]; +var current_test = 0; + + +var httpserver = null; +function run_test() { + + httpserver = new HttpServer() + httpserver.start(-1); + + const prefs = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefBranch); + prefs.setCharPref("network.proxy.http", "localhost"); + prefs.setIntPref("network.proxy.http_port", httpserver.identity.primaryPort); + prefs.setCharPref("network.proxy.no_proxies_on", ""); + prefs.setIntPref("network.proxy.type", 1); + + tests[0](); +} + +var expectedRequests = 0; // Number of HTTP requests that are expected +var requestsMade = 0; // The number of requests that were made +var expectedResponse = 0; // The response code + // Note that any test failures in the HTTP handler + // will manifest as a 500 response code + + +// Common test setup +// Parameters: +// path - path component of the URL +// handler - http handler function for the httpserver +// requests - expected number oh http requests +// response - expected http response code +// clearCache - clear the authentication cache before running the test +function setupTest( path, handler, requests, response, clearCache) { + + requestsMade = 0; + expectedRequests = requests; + expectedResponse = response; + + // clear the auth cache if requested + if (clearCache) { + do_print( "Clearing auth cache"); + Cc["@mozilla.org/network/http-auth-manager;1"] + .getService(Ci.nsIHttpAuthManager) + .clearAll(); + } + + var chan = makeChan( URL + path, URL); + httpserver.registerPathHandler( path, handler ); + chan.notificationCallbacks = new Requestor(); + chan.asyncOpen2(new TestListener()); + + return chan; +} + +// Happy code path +// Succesful proxy auth. +function test_happy_path() { + do_print("RUNNING TEST: test_happy_path"); + var chan = setupTest( "/auth", successfulAuth, 3, 200, 1); + + do_test_pending(); +} + +// Failed proxy authentication +function test_failed_auth() { + do_print("RUNNING TEST:failed auth "); + var chan = setupTest( "/auth", failedAuth, 4, 407, 1); + + do_test_pending(); +} + +var ntlmTypeOneCount = 0; // The number of NTLM type one messages received +var exptTypeOneCount = 0; // The number of NTLM type one messages that should be received + +var ntlmTypeTwoCount = 0; // The number of NTLM type two messages received +var exptTypeTwoCount = 0; // The number of NTLM type two messages that should received +// Test connection reset, after successful auth +function test_connection_reset() { + do_print("RUNNING TEST:connection reset "); + ntlmTypeOneCount = 0; + ntlmTypeTwoCount = 0; + exptTypeOneCount = 1; + exptTypeTwoCount = 1; + var chan = setupTest( "/auth", connectionReset, 2, 500, 1); + + do_test_pending(); +} + +// Test connection reset after sending a negotiate. +function test_connection_reset02() { + do_print("RUNNING TEST:connection reset "); + ntlmTypeOneCount = 0; + ntlmTypeTwoCount = 0; + exptTypeOneCount = 1; + exptTypeTwoCount = 0; + var chan = setupTest( "/auth", connectionReset02, 1, 500, 1); + + do_test_pending(); +} diff --git a/netwerk/test/unit/test_ntlm_web_auth.js b/netwerk/test/unit/test_ntlm_web_auth.js new file mode 100644 index 0000000000000..a29bcb4a7aaec --- /dev/null +++ b/netwerk/test/unit/test_ntlm_web_auth.js @@ -0,0 +1,261 @@ +// Unit tests for a NTLM authenticated web server. +// +// Currently the tests do not determine whether the Authentication dialogs have +// been displayed. +// +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + httpserver.identity.primaryPort; +}); + +const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2; +const nsIAuthInformation = Components.interfaces.nsIAuthInformation; + + +function AuthPrompt() { +} + +AuthPrompt.prototype = { + user: "guest", + pass: "guest", + + QueryInterface: function authprompt_qi(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIAuthPrompt2)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + promptAuth: function ap_promptAuth(channel, level, authInfo) { + authInfo.username = this.user; + authInfo.password = this.pass; + + return true; + }, + + asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + } +}; + +function Requestor() { +} + +Requestor.prototype = { + QueryInterface: function requestor_qi(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIInterfaceRequestor)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + getInterface: function requestor_gi(iid) { + if ( iid.equals(Components.interfaces.nsIAuthPrompt2)) { + // Allow the prompt to store state by caching it here + if (!this.prompt) + this.prompt = new AuthPrompt(); + return this.prompt; + } + + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + prompt: null +}; + +function makeChan(url, loadingUrl) { + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + var principal = ssm.createCodebasePrincipal(ios.newURI(loadingUrl, null, null), {}); + return NetUtil.newChannel( + { uri: url + , loadingPrincipal: principal + , securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL + , contentPolicyType: Components.interfaces.nsIContentPolicy.TYPE_OTHER + }); +} + +function TestListener() { +} +TestListener.prototype.onStartRequest = function(request, context) { + + // Need to do the instanceof to allow request.responseStatus + // to be read. + if (!(request instanceof Components.interfaces.nsIHttpChannel)) + do_print("Expecting an HTTP channel"); + + Assert.equal( expectedResponse, request.responseStatus,"HTTP Status code") ; +} +TestListener.prototype.onStopRequest = function(request, context, status) { + + Assert.equal(expectedRequests, requestsMade, "Number of requests made "); + + if (current_test < (tests.length - 1)) { + current_test++; + tests[current_test](); + } else { + do_test_pending(); + httpserver.stop(do_test_finished); + } + + do_test_finished(); + +} +TestListener.prototype.onDataAvaiable = function(request, context, stream, offset, count) { + var data = read_stream(stream, count); +} + +// NTLM Messages, for the received type 1 and 3 messages only check that they +// are of the expected type. +const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; +const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; +const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; +const NTLM_PREFIX_LEN = 21; + +const NTLM_CHALLENGE = NTLM_TYPE2_PREFIX + + "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" + + "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" + + "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" + + "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="; + +// Web server responses for the happy path scenario. +// i.e. successful web server auth +// +function successfulAuth( metadata, response) { + switch (requestsMade) { + case 0: + // Web Server - Initial request + // Will respond with a 401 to start web server auth sequence + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + response.setHeader( "WWW-Authenticate", "NTLM", false); + break; + case 1: + // Web Server - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + response.setHeader( "WWW-Authenticate", NTLM_CHALLENGE, false); + break; + case 2: + // Web Server - Expecting a type 3 Authenticate message from the client + var authorization = metadata.getHeader( "Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine( metadata.httpVersion, 200, "Successful"); + break; + default: + // We should be authenticated and further requests are permitted + var authorization = metadata.getHeader( "Authorization"); + Assert.isnull( authorization); + response.setStatusLine( metadata.httpVersion, 200, "Successful"); + } + requestsMade++; +} + +// web server responses simulating an unsuccessful web server auth +function failedAuth( metadata, response) { + switch (requestsMade) { + case 0: + // Web Server - First request return a 401 to start auth sequence + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + response.setHeader( "WWW-Authenticate", "NTLM", false); + break; + case 1: + // Web Server - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader( "Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + response.setHeader( "WWW-Authenticate", NTLM_CHALLENGE, false); + break; + case 2: + // Web Server - Expecting a type 3 Authenticate message from the client + // Respond with a 401 to restart the auth sequence. + var authorization = metadata.getHeader( "Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); + break; + default: + // We should not get called past step 2 + // Strictly speaking the connection should not be used again + // commented out for testing + // do_print( "ERROR: NTLM Auth failed connection should not be reused"); + //Assert.fail(); + response.setHeader( "WWW-Authenticate", "NTLM", false); + } + requestsMade++; +} + +var tests = [ + test_happy_path, + test_failed_auth +]; +var current_test = 0; + + +var httpserver = null; +function run_test() { + + httpserver = new HttpServer() + httpserver.start(-1); + + tests[0](); +} + +var expectedRequests = 0; // Number of HTTP requests that are expected +var requestsMade = 0; // The number of requests that were made +var expectedResponse = 0; // The response code + // Note that any test failures in the HTTP handler + // will manifest as a 500 response code + +// Common test setup +// Parameters: +// path - path component of the URL +// handler - http handler function for the httpserver +// requests - expected number oh http requests +// response - expected http response code +// clearCache - clear the authentication cache before running the test +function setupTest( path, handler, requests, response, clearCache) { + + requestsMade = 0; + expectedRequests = requests; + expectedResponse = response; + + // clear the auth cache if requested + if (clearCache) { + do_print( "Clearing auth cache"); + Cc["@mozilla.org/network/http-auth-manager;1"] + .getService(Ci.nsIHttpAuthManager) + .clearAll(); + } + + var chan = makeChan( URL + path, URL); + httpserver.registerPathHandler( path, handler ); + chan.notificationCallbacks = new Requestor(); + chan.asyncOpen2(new TestListener()); + + return chan; +} + +// Happy code path +// Succesful web server auth. +function test_happy_path() { + do_print("RUNNING TEST: test_happy_path"); + var chan = setupTest( "/auth", successfulAuth, 3, 200, 1); + + do_test_pending(); +} + +// Unsuccessful web server sign on +function test_failed_auth() { + do_print("RUNNING TEST: test_failed_auth"); + var chan = setupTest( "/auth", failedAuth, 3, 401, 1); + + do_test_pending(); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index d70dfd7e0f686..be573056e9cae 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -380,6 +380,9 @@ skip-if = win10_2004 && bits == 64 # Bug 1718292 [test_throttling.js] [test_separate_connections.js] [test_trackingProtection_annotateChannels.js] +[test_ntlm_web_auth.js] +[test_ntlm_proxy_auth.js] +[test_ntlm_proxy_and_web_auth.js] [test_race_cache_with_network.js] [test_rcwn_always_cache_new_content.js] [test_channel_priority.js] From fcc41d7278c453df7091235de65b34f032b78d6e Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Thu, 21 Oct 2021 11:02:10 +0000 Subject: [PATCH 18/35] Bug 1160000 - Fix up NTLM tests r=necko-reviewers,dragana I just got the previously added tests to run properly. While it would be nice to make them clean and modern I think it's not worth the additional effort - just increasing the code coverage should be enough. Differential Revision: https://phabricator.services.mozilla.com/D129008 --- .../test/unit/test_ntlm_proxy_and_web_auth.js | 521 +++++++++-------- netwerk/test/unit/test_ntlm_proxy_auth.js | 522 +++++++++--------- netwerk/test/unit/test_ntlm_web_auth.js | 328 ++++++----- 3 files changed, 658 insertions(+), 713 deletions(-) diff --git a/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js b/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js index 5311e5b407c0f..87e92678ce9db 100644 --- a/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js +++ b/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js @@ -4,30 +4,23 @@ // Currently the tests do not determine whether the Authentication dialogs have // been displayed. // -Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyGetter(this, "URL", function() { - return "http://localhost:" + httpserver.identity.primaryPort; -}); +"use strict"; -const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2; -const nsIAuthInformation = Components.interfaces.nsIAuthInformation; +const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); +ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + httpserver.identity.primaryPort; +}); -function AuthPrompt() { -} +function AuthPrompt() {} AuthPrompt.prototype = { user: "guest", pass: "guest", - QueryInterface: function authprompt_qi(iid) { - if (iid.equals(Components.interfaces.nsISupports) || - iid.equals(Components.interfaces.nsIAuthPrompt2)) - return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - }, + QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]), promptAuth: function ap_promptAuth(channel, level, authInfo) { authInfo.username = this.user; @@ -37,278 +30,271 @@ AuthPrompt.prototype = { }, asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - } + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, }; -function Requestor() { -} +function Requestor() {} Requestor.prototype = { - QueryInterface: function requestor_qi(iid) { - if (iid.equals(Components.interfaces.nsISupports) || - iid.equals(Components.interfaces.nsIInterfaceRequestor)) - return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - getInterface: function requestor_gi(iid) { - if ( iid.equals(Components.interfaces.nsIAuthPrompt2)) { - // Allow the prompt to store state by caching it here - if (!this.prompt) - this.prompt = new AuthPrompt(); - return this.prompt; - } - - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - prompt: null + QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]), + + getInterface: function requestor_gi(iid) { + if (iid.equals(Ci.nsIAuthPrompt2)) { + // Allow the prompt to store state by caching it here + if (!this.prompt) { + this.prompt = new AuthPrompt(); + } + return this.prompt; + } + + throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); + }, + + prompt: null, }; function makeChan(url, loadingUrl) { - var ios = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - var principal = ssm.createCodebasePrincipal(ios.newURI(loadingUrl, null, null), {}); - return NetUtil.newChannel( - { uri: url - , loadingPrincipal: principal - , securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL - , contentPolicyType: Components.interfaces.nsIContentPolicy.TYPE_OTHER - }); + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].getService( + Ci.nsIScriptSecurityManager + ); + var principal = ssm.createContentPrincipal(ios.newURI(loadingUrl), {}); + return NetUtil.newChannel({ + uri: url, + loadingPrincipal: principal, + securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, + contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, + }); } -function TestListener() { +function TestListener(resolve) { + this.resolve = resolve; } TestListener.prototype.onStartRequest = function(request, context) { + // Need to do the instanceof to allow request.responseStatus + // to be read. + if (!(request instanceof Ci.nsIHttpChannel)) { + dump("Expecting an HTTP channel"); + } - // Need to do the instanceof to allow request.responseStatus - // to be read. - if (!(request instanceof Components.interfaces.nsIHttpChannel)) - do_print("Expecting an HTTP channel"); - - Assert.equal( expectedResponse, request.responseStatus,"HTTP Status code") ; -} + Assert.equal(expectedResponse, request.responseStatus, "HTTP Status code"); +}; TestListener.prototype.onStopRequest = function(request, context, status) { + Assert.equal(expectedRequests, requestsMade, "Number of requests made "); - Assert.equal(expectedRequests, requestsMade, "Number of requests made "); - - if (current_test < (tests.length - 1)) { - current_test++; - tests[current_test](); - } else { - do_test_pending(); - httpserver.stop(do_test_finished); - } - - do_test_finished(); - -} -TestListener.prototype.onDataAvaiable = function(request, context, stream, offset, count) { - var data = read_stream(stream, count); -} + this.resolve(); +}; +TestListener.prototype.onDataAvaiable = function( + request, + context, + stream, + offset, + count +) { + read_stream(stream, count); +}; // NTLM Messages, for the received type 1 and 3 messages only check that they // are of the expected type. const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; -const NTLM_PREFIX_LEN = 21; - -const NTLM_CHALLENGE = NTLM_TYPE2_PREFIX + - "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" + - "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" + - "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" + - "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="; - -const PROXY_CHALLENGE = NTLM_TYPE2_PREFIX + - "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" + - "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" + - "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" + - "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA"; - - +const NTLM_PREFIX_LEN = 21; + +const NTLM_CHALLENGE = + NTLM_TYPE2_PREFIX + + "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" + + "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" + + "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" + + "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="; + +const PROXY_CHALLENGE = + NTLM_TYPE2_PREFIX + + "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" + + "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" + + "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" + + "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA"; // Proxy and Web server responses for the happy path scenario. // i.e. successful proxy auth and successful web server auth // -function authHandler( metadata, response) { - switch (requestsMade) { +function authHandler(metadata, response) { + switch (requestsMade) { case 0: - // Proxy - First request to the Proxy resppond with a 407 to start auth - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - First request to the Proxy resppond with a 407 to start auth + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); - break; + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); + break; case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Will respond with a 401 to start web server auth sequence - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - response.setHeader( "WWW-Authenticate", "NTLM", false); - break; + // Proxy - Expecting a type 3 Authenticate message from the client + // Will respond with a 401 to start web server auth sequence + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", "NTLM", false); + break; case 3: - // Web Server - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - response.setHeader( "WWW-Authenticate", NTLM_CHALLENGE, false); - break; + // Web Server - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false); + break; case 4: - // Web Server - Expecting a type 3 Authenticate message from the client - var authorization = metadata.getHeader( "Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine( metadata.httpVersion, 200, "Successful"); - break; + // Web Server - Expecting a type 3 Authenticate message from the client + var authorization = metadata.getHeader("Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine(metadata.httpVersion, 200, "Successful"); + break; default: - // We should be authenticated and further requests are permitted - var authorization = metadata.getHeader( "Authorization"); - var authorization = metadata.getHeader( "Proxy-Authorization"); - Assert.isnull( authorization); - response.setStatusLine( metadata.httpVersion, 200, "Successful"); - } - requestsMade++; + // We should be authenticated and further requests are permitted + var authorization = metadata.getHeader("Authorization"); + var authorization = metadata.getHeader("Proxy-Authorization"); + Assert.isnull(authorization); + response.setStatusLine(metadata.httpVersion, 200, "Successful"); + } + requestsMade++; } // Proxy responses simulating an invalid proxy password // Note: that the connection should not be reused after the // proxy auth fails. // -function authHandlerInvalidProxyPassword( metadata, response) { - switch (requestsMade) { +function authHandlerInvalidProxyPassword(metadata, response) { + switch (requestsMade) { case 0: - // Proxy - First request respond with a 407 to initiate auth sequence - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - First request respond with a 407 to initiate auth sequence + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); - break; + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); + break; case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Respond with a 407 to indicate invalid credentials - // - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - Expecting a type 3 Authenticate message from the client + // Respond with a 407 to indicate invalid credentials + // + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; default: - // Strictly speaking the connection should not be reused at this point - // and reaching here should be an error, but have commented out for now - //do_print( "ERROR: NTLM Proxy Authentication, connection should not be reused"); - //Assert.fail(); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - } - requestsMade++; + // Strictly speaking the connection should not be reused at this point + // and reaching here should be an error, but have commented out for now + //dump( "ERROR: NTLM Proxy Authentication, connection should not be reused"); + //Assert.fail(); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + } + requestsMade++; } // Proxy and web server responses simulating a successful Proxy auth // and a failed web server auth // Note: the connection should not be reused once the password failure is // detected -function authHandlerInvalidWebPassword( metadata, response) { - switch (requestsMade) { +function authHandlerInvalidWebPassword(metadata, response) { + switch (requestsMade) { case 0: - // Proxy - First request return a 407 to start Proxy auth - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - First request return a 407 to start Proxy auth + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", NTLM_CHALLENGE, false); - break; + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", NTLM_CHALLENGE, false); + break; case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Responds with a 401 to start web server auth - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - response.setHeader( "WWW-Authenticate", "NTLM", false); - break; + // Proxy - Expecting a type 3 Authenticate message from the client + // Responds with a 401 to start web server auth + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", "NTLM", false); + break; case 3: - // Web Server - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - response.setHeader( "WWW-Authenticate", NTLM_CHALLENGE, false); - break; + // Web Server - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false); + break; case 4: - // Web Server - Expecting a type 3 Authenticate message from the client - // Respond with a 401 to restart the auth sequence. - var authorization = metadata.getHeader( "Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - break; + // Web Server - Expecting a type 3 Authenticate message from the client + // Respond with a 401 to restart the auth sequence. + var authorization = metadata.getHeader("Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + break; default: - // We should not get called past step 4 - do_print( "ERROR: NTLM Auth failed connection should not be reused"); - Assert.fail(); - } - requestsMade++; + // We should not get called past step 4 + dump("ERROR: NTLM Auth failed connection should not be reused"); + Assert.fail(); + } + requestsMade++; } // Tests to run test_bad_proxy_pass and test_bad_web_pass are split into two stages // so that once we determine how detect password dialog displays we can check // that the are displayed correctly, i.e. proxy password should not be prompted // for when retrying the web server password -var tests = [ - test_happy_path, - test_bad_proxy_pass_stage01, - test_bad_proxy_pass_stage02, - test_bad_web_pass_stage01, - test_bad_web_pass_stage02 -]; -var current_test = 0; - var httpserver = null; -function run_test() { - - httpserver = new HttpServer() - httpserver.start(-1); - - const prefs = Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefBranch); - prefs.setCharPref("network.proxy.http", "localhost"); - prefs.setIntPref("network.proxy.http_port", httpserver.identity.primaryPort); - prefs.setCharPref("network.proxy.no_proxies_on", ""); - prefs.setIntPref("network.proxy.type", 1); - - tests[0](); +function setup() { + httpserver = new HttpServer(); + httpserver.start(-1); + + Services.prefs.setCharPref("network.proxy.http", "localhost"); + Services.prefs.setIntPref( + "network.proxy.http_port", + httpserver.identity.primaryPort + ); + Services.prefs.setCharPref("network.proxy.no_proxies_on", ""); + Services.prefs.setIntPref("network.proxy.type", 1); + Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); + + registerCleanupFunction(async () => { + Services.prefs.clearUserPref("network.proxy.http"); + Services.prefs.clearUserPref("network.proxy.http_port"); + Services.prefs.clearUserPref("network.proxy.no_proxies_on"); + Services.prefs.clearUserPref("network.proxy.type"); + Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); + + await httpserver.stop(); + }); } +setup(); -var expectedRequests = 0; // Number of HTTP requests that are expected -var requestsMade = 0; // The number of requests that were made -var expectedResponse = 0; // The response code - // Note that any test failures in the HTTP handler - // will manifest as a 500 response code +var expectedRequests = 0; // Number of HTTP requests that are expected +var requestsMade = 0; // The number of requests that were made +var expectedResponse = 0; // The response code +// Note that any test failures in the HTTP handler +// will manifest as a 500 response code // Common test setup // Parameters: @@ -317,63 +303,52 @@ var expectedResponse = 0; // The response code // requests - expected number oh http requests // response - expected http response code // clearCache - clear the authentication cache before running the test -function setupTest( path, handler, requests, response, clearCache) { - - requestsMade = 0; - expectedRequests = requests; - expectedResponse = response; - - // clear the auth cache if requested - if (clearCache) { - do_print( "Clearing auth cache"); - Cc["@mozilla.org/network/http-auth-manager;1"] - .getService(Ci.nsIHttpAuthManager) - .clearAll(); - } +function setupTest(path, handler, requests, response, clearCache) { + requestsMade = 0; + expectedRequests = requests; + expectedResponse = response; + + // clear the auth cache if requested + if (clearCache) { + dump("Clearing auth cache"); + Cc["@mozilla.org/network/http-auth-manager;1"] + .getService(Ci.nsIHttpAuthManager) + .clearAll(); + } - var chan = makeChan( URL + path, URL); - httpserver.registerPathHandler( path, handler ); + return new Promise(resolve => { + var chan = makeChan(URL + path, URL); + httpserver.registerPathHandler(path, handler); chan.notificationCallbacks = new Requestor(); - chan.asyncOpen2(new TestListener()); - - return chan; + chan.asyncOpen(new TestListener(resolve)); + }); } // Happy code path // Succesful proxy and web server auth. -function test_happy_path() { - do_print("RUNNING TEST: test_happy_path"); - var chan = setupTest( "/auth", authHandler, 5, 200, 1); - - do_test_pending(); -} +add_task(async function test_happy_path() { + dump("RUNNING TEST: test_happy_path"); + await setupTest("/auth", authHandler, 5, 200, 1); +}); // Failed proxy authentication -function test_bad_proxy_pass_stage01() { - do_print("RUNNING TEST: test_bad_proxy_pass_stage01"); - var chan = setupTest( "/auth", authHandlerInvalidProxyPassword, 4, 407, 1); - - do_test_pending(); -} +add_task(async function test_bad_proxy_pass_stage01() { + dump("RUNNING TEST: test_bad_proxy_pass_stage01"); + await setupTest("/auth", authHandlerInvalidProxyPassword, 4, 407, 1); +}); // Successful logon after failed proxy auth -function test_bad_proxy_pass_stage02() { - do_print("RUNNING TEST: test_bad_proxy_pass_stage02"); - var chan = setupTest( "/auth", authHandler, 5, 200, 0); - - do_test_pending(); -} +add_task(async function test_bad_proxy_pass_stage02() { + dump("RUNNING TEST: test_bad_proxy_pass_stage02"); + await setupTest("/auth", authHandler, 5, 200, 0); +}); // successful proxy logon, unsuccessful web server sign on -function test_bad_web_pass_stage01() { - do_print("RUNNING TEST: test_bad_web_pass_stage01"); - var chan = setupTest( "/auth", authHandlerInvalidWebPassword, 5, 401, 1); - - do_test_pending(); -} +add_task(async function test_bad_web_pass_stage01() { + dump("RUNNING TEST: test_bad_web_pass_stage01"); + await setupTest("/auth", authHandlerInvalidWebPassword, 5, 401, 1); +}); // successful logon after failed web server auth. -function test_bad_web_pass_stage02() { - do_print("RUNNING TEST: test_bad_web_pass_stage02"); - var chan = setupTest( "/auth", authHandler, 5, 200, 0); - - do_test_pending(); -} +add_task(async function test_bad_web_pass_stage02() { + dump("RUNNING TEST: test_bad_web_pass_stage02"); + await setupTest("/auth", authHandler, 5, 200, 0); +}); diff --git a/netwerk/test/unit/test_ntlm_proxy_auth.js b/netwerk/test/unit/test_ntlm_proxy_auth.js index c569ecff78b0e..ee8ff1cd868b2 100644 --- a/netwerk/test/unit/test_ntlm_proxy_auth.js +++ b/netwerk/test/unit/test_ntlm_proxy_auth.js @@ -3,30 +3,23 @@ // Currently the tests do not determine whether the Authentication dialogs have // been displayed. // -Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/NetUtil.jsm"); + +"use strict"; + +const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); +ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyGetter(this, "URL", function() { - return "http://localhost:" + httpserver.identity.primaryPort; + return "http://localhost:" + httpserver.identity.primaryPort; }); -const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2; -const nsIAuthInformation = Components.interfaces.nsIAuthInformation; - - -function AuthPrompt() { -} +function AuthPrompt() {} AuthPrompt.prototype = { user: "guest", pass: "guest", - QueryInterface: function authprompt_qi(iid) { - if (iid.equals(Components.interfaces.nsISupports) || - iid.equals(Components.interfaces.nsIAuthPrompt2)) - return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - }, + QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]), promptAuth: function ap_promptAuth(channel, level, authInfo) { authInfo.username = this.user; @@ -36,266 +29,266 @@ AuthPrompt.prototype = { }, asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - } + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, }; -function Requestor() { -} +function Requestor() {} Requestor.prototype = { - QueryInterface: function requestor_qi(iid) { - if (iid.equals(Components.interfaces.nsISupports) || - iid.equals(Components.interfaces.nsIInterfaceRequestor)) - return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - getInterface: function requestor_gi(iid) { - if ( iid.equals(Components.interfaces.nsIAuthPrompt2)) { - // Allow the prompt to store state by caching it here - if (!this.prompt) - this.prompt = new AuthPrompt(); - return this.prompt; - } - - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - prompt: null + QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]), + + getInterface: function requestor_gi(iid) { + if (iid.equals(Ci.nsIAuthPrompt2)) { + // Allow the prompt to store state by caching it here + if (!this.prompt) { + this.prompt = new AuthPrompt(); + } + return this.prompt; + } + + throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); + }, + + prompt: null, }; function makeChan(url, loadingUrl) { - var ios = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - var principal = ssm.createCodebasePrincipal(ios.newURI(loadingUrl, null, null), {}); - return NetUtil.newChannel( - { uri: url - , loadingPrincipal: principal - , securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL - , contentPolicyType: Components.interfaces.nsIContentPolicy.TYPE_OTHER - }); + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].getService( + Ci.nsIScriptSecurityManager + ); + var principal = ssm.createContentPrincipal(ios.newURI(loadingUrl), {}); + return NetUtil.newChannel({ + uri: url, + loadingPrincipal: principal, + securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, + contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, + }); } -function TestListener() { +function TestListener(resolve) { + this.resolve = resolve; } TestListener.prototype.onStartRequest = function(request, context) { + // Need to do the instanceof to allow request.responseStatus + // to be read. + if (!(request instanceof Ci.nsIHttpChannel)) { + dump("Expecting an HTTP channel"); + } - // Need to do the instanceof to allow request.responseStatus - // to be read. - if (!(request instanceof Components.interfaces.nsIHttpChannel)) - do_print("Expecting an HTTP channel"); - - Assert.equal( expectedResponse, request.responseStatus,"HTTP Status code") ; -} + Assert.equal(expectedResponse, request.responseStatus, "HTTP Status code"); +}; TestListener.prototype.onStopRequest = function(request, context, status) { - - Assert.equal(expectedRequests, requestsMade, "Number of requests made "); - Assert.equal(exptTypeOneCount, ntlmTypeOneCount, "Number of type one messages received"); - Assert.equal(exptTypeTwoCount, ntlmTypeTwoCount, "Number of type two messages received"); - - if (current_test < (tests.length - 1)) { - current_test++; - tests[current_test](); - } else { - do_test_pending(); - httpserver.stop(do_test_finished); - } - - do_test_finished(); - -} -TestListener.prototype.onDataAvaiable = function(request, context, stream, offset, count) { - var data = read_stream(stream, count); -} + Assert.equal(expectedRequests, requestsMade, "Number of requests made "); + Assert.equal( + exptTypeOneCount, + ntlmTypeOneCount, + "Number of type one messages received" + ); + Assert.equal( + exptTypeTwoCount, + ntlmTypeTwoCount, + "Number of type two messages received" + ); + + this.resolve(); +}; +TestListener.prototype.onDataAvaiable = function( + request, + context, + stream, + offset, + count +) { + read_stream(stream, count); +}; // NTLM Messages, for the received type 1 and 3 messages only check that they // are of the expected type. const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; -const NTLM_PREFIX_LEN = 21; - -const PROXY_CHALLENGE = NTLM_TYPE2_PREFIX + - "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" + - "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" + - "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" + - "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA"; - +const NTLM_PREFIX_LEN = 21; +const PROXY_CHALLENGE = + NTLM_TYPE2_PREFIX + + "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" + + "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" + + "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" + + "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA"; // Proxy responses for the happy path scenario. // i.e. successful proxy auth // -function successfulAuth( metadata, response) { - switch (requestsMade) { +function successfulAuth(metadata, response) { + switch (requestsMade) { case 0: - // Proxy - First request to the Proxy resppond with a 407 to start auth - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - First request to the Proxy resppond with a 407 to start auth + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); - break; + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); + break; case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Will respond with a 401 to start web server auth sequence - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine( metadata.httpVersion, 200, "Successful"); - break; + // Proxy - Expecting a type 3 Authenticate message from the client + // Will respond with a 401 to start web server auth sequence + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine(metadata.httpVersion, 200, "Successful"); + break; default: - // We should be authenticated and further requests are permitted - var authorization = metadata.getHeader( "Proxy-Authorization"); - Assert.isnull( authorization); - response.setStatusLine( metadata.httpVersion, 200, "Successful"); - } - requestsMade++; + // We should be authenticated and further requests are permitted + var authorization = metadata.getHeader("Proxy-Authorization"); + Assert.isnull(authorization); + response.setStatusLine(metadata.httpVersion, 200, "Successful"); + } + requestsMade++; } // Proxy responses simulating an invalid proxy password // Note: that the connection should not be reused after the // proxy auth fails. // -function failedAuth( metadata, response) { - switch (requestsMade) { +function failedAuth(metadata, response) { + switch (requestsMade) { case 0: - // Proxy - First request respond with a 407 to initiate auth sequence - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - First request respond with a 407 to initiate auth sequence + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); - break; + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); + break; case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Respond with a 407 to indicate invalid credentials - // - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - Expecting a type 3 Authenticate message from the client + // Respond with a 407 to indicate invalid credentials + // + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; default: - // Strictly speaking the connection should not be reused at this point - // commenting out for now. - // do_print( "ERROR: NTLM Proxy Authentication, connection should not be reused"); - // assert.fail(); - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - } - requestsMade++; + // Strictly speaking the connection should not be reused at this point + // commenting out for now. + dump("ERROR: NTLM Proxy Authentication, connection should not be reused"); + // assert.fail(); + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + } + requestsMade++; } // // Simulate a connection reset once the connection has been authenticated // Detects bug 486508 // -function connectionReset( metadata, response) { - switch (requestsMade) { +function connectionReset(metadata, response) { + switch (requestsMade) { case 0: - // Proxy - First request to the Proxy resppond with a 407 to start auth - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - First request to the Proxy resppond with a 407 to start auth + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - ntlmTypeOneCount++; - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); - break; + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + ntlmTypeOneCount++; + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); + break; case 2: - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - ntlmTypeTwoCount++; - response.seizePower(); - response.bodyOutPutStream.close(); - response.finish(); - break; + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + ntlmTypeTwoCount++; + response.seizePower(); + response.bodyOutPutStream.close(); + response.finish(); + break; default: - // Should not get any further requests on this channel - do_print( "ERROR: NTLM Proxy Authentication, connection should not be reused"); - assert.fail(); - } - requestsMade++; + // Should not get any further requests on this channel + dump("ERROR: NTLM Proxy Authentication, connection should not be reused"); + Assert.ok(false); + } + requestsMade++; } // // Reset the connection after a nogotiate message has been received // -function connectionReset02( metadata, response) { - switch (requestsMade) { +function connectionReset02(metadata, response) { + switch (requestsMade) { case 0: - // Proxy - First request to the Proxy resppond with a 407 to start auth - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", "NTLM", false); - break; + // Proxy - First request to the Proxy resppond with a 407 to start auth + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", "NTLM", false); + break; case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - ntlmTypeOneCount++; - response.setStatusLine( metadata.httpVersion, 407, "Unauthorized"); - response.setHeader( "Proxy-Authenticate", PROXY_CHALLENGE, false); - response.finish(); - response.seizePower(); - response.bodyOutPutStream.close(); - break; + // Proxy - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Proxy-Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + ntlmTypeOneCount++; + response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); + response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); + response.finish(); + response.seizePower(); + response.bodyOutPutStream.close(); + break; default: - // Should not get any further requests on this channel - do_print( "ERROR: NTLM Proxy Authentication, connection should not be reused"); - assert.fail(); - } - requestsMade++; + // Should not get any further requests on this channel + dump("ERROR: NTLM Proxy Authentication, connection should not be reused"); + Assert.ok(false); + } + requestsMade++; } -var tests = [ - test_happy_path, - test_failed_auth, - test_connection_reset, - test_connection_reset02 -]; -var current_test = 0; - var httpserver = null; -function run_test() { - - httpserver = new HttpServer() - httpserver.start(-1); - - const prefs = Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefBranch); - prefs.setCharPref("network.proxy.http", "localhost"); - prefs.setIntPref("network.proxy.http_port", httpserver.identity.primaryPort); - prefs.setCharPref("network.proxy.no_proxies_on", ""); - prefs.setIntPref("network.proxy.type", 1); - - tests[0](); +function setup() { + httpserver = new HttpServer(); + httpserver.start(-1); + + Services.prefs.setCharPref("network.proxy.http", "localhost"); + Services.prefs.setIntPref( + "network.proxy.http_port", + httpserver.identity.primaryPort + ); + Services.prefs.setCharPref("network.proxy.no_proxies_on", ""); + Services.prefs.setIntPref("network.proxy.type", 1); + Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); + + registerCleanupFunction(async () => { + Services.prefs.clearUserPref("network.proxy.http"); + Services.prefs.clearUserPref("network.proxy.http_port"); + Services.prefs.clearUserPref("network.proxy.no_proxies_on"); + Services.prefs.clearUserPref("network.proxy.type"); + Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); + + await httpserver.stop(); + }); } +setup(); -var expectedRequests = 0; // Number of HTTP requests that are expected -var requestsMade = 0; // The number of requests that were made -var expectedResponse = 0; // The response code - // Note that any test failures in the HTTP handler - // will manifest as a 500 response code - +var expectedRequests = 0; // Number of HTTP requests that are expected +var requestsMade = 0; // The number of requests that were made +var expectedResponse = 0; // The response code +// Note that any test failures in the HTTP handler +// will manifest as a 500 response code // Common test setup // Parameters: @@ -304,70 +297,61 @@ var expectedResponse = 0; // The response code // requests - expected number oh http requests // response - expected http response code // clearCache - clear the authentication cache before running the test -function setupTest( path, handler, requests, response, clearCache) { - - requestsMade = 0; - expectedRequests = requests; - expectedResponse = response; - - // clear the auth cache if requested - if (clearCache) { - do_print( "Clearing auth cache"); - Cc["@mozilla.org/network/http-auth-manager;1"] - .getService(Ci.nsIHttpAuthManager) - .clearAll(); - } +function setupTest(path, handler, requests, response, clearCache) { + requestsMade = 0; + expectedRequests = requests; + expectedResponse = response; + + // clear the auth cache if requested + if (clearCache) { + dump("Clearing auth cache"); + Cc["@mozilla.org/network/http-auth-manager;1"] + .getService(Ci.nsIHttpAuthManager) + .clearAll(); + } - var chan = makeChan( URL + path, URL); - httpserver.registerPathHandler( path, handler ); + return new Promise(resolve => { + var chan = makeChan(URL + path, URL); + httpserver.registerPathHandler(path, handler); chan.notificationCallbacks = new Requestor(); - chan.asyncOpen2(new TestListener()); - - return chan; + chan.asyncOpen(new TestListener(resolve)); + }); } // Happy code path // Succesful proxy auth. -function test_happy_path() { - do_print("RUNNING TEST: test_happy_path"); - var chan = setupTest( "/auth", successfulAuth, 3, 200, 1); - - do_test_pending(); -} +add_task(async function test_happy_path() { + dump("RUNNING TEST: test_happy_path"); + await setupTest("/auth", successfulAuth, 3, 200, 1); +}); // Failed proxy authentication -function test_failed_auth() { - do_print("RUNNING TEST:failed auth "); - var chan = setupTest( "/auth", failedAuth, 4, 407, 1); - - do_test_pending(); -} +add_task(async function test_failed_auth() { + dump("RUNNING TEST:failed auth "); + await setupTest("/auth", failedAuth, 4, 407, 1); +}); -var ntlmTypeOneCount = 0; // The number of NTLM type one messages received -var exptTypeOneCount = 0; // The number of NTLM type one messages that should be received +var ntlmTypeOneCount = 0; // The number of NTLM type one messages received +var exptTypeOneCount = 0; // The number of NTLM type one messages that should be received -var ntlmTypeTwoCount = 0; // The number of NTLM type two messages received -var exptTypeTwoCount = 0; // The number of NTLM type two messages that should received +var ntlmTypeTwoCount = 0; // The number of NTLM type two messages received +var exptTypeTwoCount = 0; // The number of NTLM type two messages that should received // Test connection reset, after successful auth -function test_connection_reset() { - do_print("RUNNING TEST:connection reset "); - ntlmTypeOneCount = 0; - ntlmTypeTwoCount = 0; - exptTypeOneCount = 1; - exptTypeTwoCount = 1; - var chan = setupTest( "/auth", connectionReset, 2, 500, 1); - - do_test_pending(); -} +add_task(async function test_connection_reset() { + dump("RUNNING TEST:connection reset "); + ntlmTypeOneCount = 0; + ntlmTypeTwoCount = 0; + exptTypeOneCount = 1; + exptTypeTwoCount = 1; + await setupTest("/auth", connectionReset, 2, 500, 1); +}); // Test connection reset after sending a negotiate. -function test_connection_reset02() { - do_print("RUNNING TEST:connection reset "); - ntlmTypeOneCount = 0; - ntlmTypeTwoCount = 0; - exptTypeOneCount = 1; - exptTypeTwoCount = 0; - var chan = setupTest( "/auth", connectionReset02, 1, 500, 1); - - do_test_pending(); -} +add_task(async function test_connection_reset02() { + dump("RUNNING TEST:connection reset "); + ntlmTypeOneCount = 0; + ntlmTypeTwoCount = 0; + exptTypeOneCount = 1; + exptTypeTwoCount = 0; + await setupTest("/auth", connectionReset02, 1, 500, 1); +}); diff --git a/netwerk/test/unit/test_ntlm_web_auth.js b/netwerk/test/unit/test_ntlm_web_auth.js index a29bcb4a7aaec..61ea61825ef3a 100644 --- a/netwerk/test/unit/test_ntlm_web_auth.js +++ b/netwerk/test/unit/test_ntlm_web_auth.js @@ -3,30 +3,23 @@ // Currently the tests do not determine whether the Authentication dialogs have // been displayed. // -Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyGetter(this, "URL", function() { - return "http://localhost:" + httpserver.identity.primaryPort; -}); +"use strict"; -const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2; -const nsIAuthInformation = Components.interfaces.nsIAuthInformation; +const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); +ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); +XPCOMUtils.defineLazyGetter(this, "URL", function() { + return "http://localhost:" + httpserver.identity.primaryPort; +}); -function AuthPrompt() { -} +function AuthPrompt() {} AuthPrompt.prototype = { user: "guest", pass: "guest", - QueryInterface: function authprompt_qi(iid) { - if (iid.equals(Components.interfaces.nsISupports) || - iid.equals(Components.interfaces.nsIAuthPrompt2)) - return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - }, + QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]), promptAuth: function ap_promptAuth(channel, level, authInfo) { authInfo.username = this.user; @@ -36,183 +29,177 @@ AuthPrompt.prototype = { }, asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - } + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, }; -function Requestor() { -} +function Requestor() {} Requestor.prototype = { - QueryInterface: function requestor_qi(iid) { - if (iid.equals(Components.interfaces.nsISupports) || - iid.equals(Components.interfaces.nsIInterfaceRequestor)) - return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - getInterface: function requestor_gi(iid) { - if ( iid.equals(Components.interfaces.nsIAuthPrompt2)) { - // Allow the prompt to store state by caching it here - if (!this.prompt) - this.prompt = new AuthPrompt(); - return this.prompt; - } - - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - prompt: null + QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]), + + getInterface: function requestor_gi(iid) { + if (iid.equals(Ci.nsIAuthPrompt2)) { + // Allow the prompt to store state by caching it here + if (!this.prompt) { + this.prompt = new AuthPrompt(); + } + return this.prompt; + } + + throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); + }, + + prompt: null, }; function makeChan(url, loadingUrl) { - var ios = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - var principal = ssm.createCodebasePrincipal(ios.newURI(loadingUrl, null, null), {}); - return NetUtil.newChannel( - { uri: url - , loadingPrincipal: principal - , securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL - , contentPolicyType: Components.interfaces.nsIContentPolicy.TYPE_OTHER - }); + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var principal = Services.scriptSecurityManager.createContentPrincipal( + ios.newURI(loadingUrl), + {} + ); + return NetUtil.newChannel({ + uri: url, + loadingPrincipal: principal, + securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, + contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, + }); } -function TestListener() { -} +function TestListener() {} TestListener.prototype.onStartRequest = function(request, context) { + // Need to do the instanceof to allow request.responseStatus + // to be read. + if (!(request instanceof Ci.nsIHttpChannel)) { + dump("Expecting an HTTP channel"); + } - // Need to do the instanceof to allow request.responseStatus - // to be read. - if (!(request instanceof Components.interfaces.nsIHttpChannel)) - do_print("Expecting an HTTP channel"); - - Assert.equal( expectedResponse, request.responseStatus,"HTTP Status code") ; -} + Assert.equal(expectedResponse, request.responseStatus, "HTTP Status code"); +}; TestListener.prototype.onStopRequest = function(request, context, status) { + Assert.equal(expectedRequests, requestsMade, "Number of requests made "); - Assert.equal(expectedRequests, requestsMade, "Number of requests made "); - - if (current_test < (tests.length - 1)) { - current_test++; - tests[current_test](); - } else { - do_test_pending(); - httpserver.stop(do_test_finished); - } - - do_test_finished(); + if (current_test < tests.length - 1) { + current_test++; + tests[current_test](); + } else { + do_test_pending(); + httpserver.stop(do_test_finished); + } -} -TestListener.prototype.onDataAvaiable = function(request, context, stream, offset, count) { - var data = read_stream(stream, count); -} + do_test_finished(); +}; +TestListener.prototype.onDataAvaiable = function( + request, + context, + stream, + offset, + count +) { + read_stream(stream, count); +}; // NTLM Messages, for the received type 1 and 3 messages only check that they // are of the expected type. const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; -const NTLM_PREFIX_LEN = 21; +const NTLM_PREFIX_LEN = 21; -const NTLM_CHALLENGE = NTLM_TYPE2_PREFIX + - "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" + - "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" + - "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" + - "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="; +const NTLM_CHALLENGE = + NTLM_TYPE2_PREFIX + + "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" + + "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" + + "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" + + "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="; // Web server responses for the happy path scenario. // i.e. successful web server auth // -function successfulAuth( metadata, response) { - switch (requestsMade) { +function successfulAuth(metadata, response) { + switch (requestsMade) { case 0: - // Web Server - Initial request - // Will respond with a 401 to start web server auth sequence - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - response.setHeader( "WWW-Authenticate", "NTLM", false); - break; + // Web Server - Initial request + // Will respond with a 401 to start web server auth sequence + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", "NTLM", false); + break; case 1: - // Web Server - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - response.setHeader( "WWW-Authenticate", NTLM_CHALLENGE, false); - break; + // Web Server - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false); + break; case 2: - // Web Server - Expecting a type 3 Authenticate message from the client - var authorization = metadata.getHeader( "Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine( metadata.httpVersion, 200, "Successful"); - break; + // Web Server - Expecting a type 3 Authenticate message from the client + var authorization = metadata.getHeader("Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); + response.setStatusLine(metadata.httpVersion, 200, "Successful"); + break; default: - // We should be authenticated and further requests are permitted - var authorization = metadata.getHeader( "Authorization"); - Assert.isnull( authorization); - response.setStatusLine( metadata.httpVersion, 200, "Successful"); - } - requestsMade++; + // We should be authenticated and further requests are permitted + var authorization = metadata.getHeader("Authorization"); + Assert.isnull(authorization); + response.setStatusLine(metadata.httpVersion, 200, "Successful"); + } + requestsMade++; } // web server responses simulating an unsuccessful web server auth -function failedAuth( metadata, response) { - switch (requestsMade) { +function failedAuth(metadata, response) { + switch (requestsMade) { case 0: - // Web Server - First request return a 401 to start auth sequence - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - response.setHeader( "WWW-Authenticate", "NTLM", false); - break; + // Web Server - First request return a 401 to start auth sequence + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", "NTLM", false); + break; case 1: - // Web Server - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader( "Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - response.setHeader( "WWW-Authenticate", NTLM_CHALLENGE, false); - break; + // Web Server - Expecting a type 1 negotiate message from the client + var authorization = metadata.getHeader("Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false); + break; case 2: - // Web Server - Expecting a type 3 Authenticate message from the client - // Respond with a 401 to restart the auth sequence. - var authorization = metadata.getHeader( "Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal( NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine( metadata.httpVersion, 401, "Unauthorized"); - break; + // Web Server - Expecting a type 3 Authenticate message from the client + // Respond with a 401 to restart the auth sequence. + var authorization = metadata.getHeader("Authorization"); + var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); + Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message"); + response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); + break; default: - // We should not get called past step 2 - // Strictly speaking the connection should not be used again - // commented out for testing - // do_print( "ERROR: NTLM Auth failed connection should not be reused"); - //Assert.fail(); - response.setHeader( "WWW-Authenticate", "NTLM", false); - } - requestsMade++; + // We should not get called past step 2 + // Strictly speaking the connection should not be used again + // commented out for testing + // dump( "ERROR: NTLM Auth failed connection should not be reused"); + //Assert.fail(); + response.setHeader("WWW-Authenticate", "NTLM", false); + } + requestsMade++; } -var tests = [ - test_happy_path, - test_failed_auth -]; +var tests = [test_happy_path, test_failed_auth]; var current_test = 0; - var httpserver = null; function run_test() { + httpserver = new HttpServer(); + httpserver.start(-1); - httpserver = new HttpServer() - httpserver.start(-1); - - tests[0](); + tests[0](); } -var expectedRequests = 0; // Number of HTTP requests that are expected -var requestsMade = 0; // The number of requests that were made -var expectedResponse = 0; // The response code - // Note that any test failures in the HTTP handler - // will manifest as a 500 response code +var expectedRequests = 0; // Number of HTTP requests that are expected +var requestsMade = 0; // The number of requests that were made +var expectedResponse = 0; // The response code +// Note that any test failures in the HTTP handler +// will manifest as a 500 response code // Common test setup // Parameters: @@ -221,41 +208,40 @@ var expectedResponse = 0; // The response code // requests - expected number oh http requests // response - expected http response code // clearCache - clear the authentication cache before running the test -function setupTest( path, handler, requests, response, clearCache) { - - requestsMade = 0; - expectedRequests = requests; - expectedResponse = response; - - // clear the auth cache if requested - if (clearCache) { - do_print( "Clearing auth cache"); - Cc["@mozilla.org/network/http-auth-manager;1"] - .getService(Ci.nsIHttpAuthManager) - .clearAll(); - } +function setupTest(path, handler, requests, response, clearCache) { + requestsMade = 0; + expectedRequests = requests; + expectedResponse = response; + + // clear the auth cache if requested + if (clearCache) { + dump("Clearing auth cache"); + Cc["@mozilla.org/network/http-auth-manager;1"] + .getService(Ci.nsIHttpAuthManager) + .clearAll(); + } - var chan = makeChan( URL + path, URL); - httpserver.registerPathHandler( path, handler ); - chan.notificationCallbacks = new Requestor(); - chan.asyncOpen2(new TestListener()); + var chan = makeChan(URL + path, URL); + httpserver.registerPathHandler(path, handler); + chan.notificationCallbacks = new Requestor(); + chan.asyncOpen(new TestListener()); - return chan; + return chan; } // Happy code path // Succesful web server auth. function test_happy_path() { - do_print("RUNNING TEST: test_happy_path"); - var chan = setupTest( "/auth", successfulAuth, 3, 200, 1); + dump("RUNNING TEST: test_happy_path"); + setupTest("/auth", successfulAuth, 3, 200, 1); - do_test_pending(); + do_test_pending(); } // Unsuccessful web server sign on function test_failed_auth() { - do_print("RUNNING TEST: test_failed_auth"); - var chan = setupTest( "/auth", failedAuth, 3, 401, 1); + dump("RUNNING TEST: test_failed_auth"); + setupTest("/auth", failedAuth, 3, 401, 1); - do_test_pending(); + do_test_pending(); } From 19f951660e518950466f72af539eb71268be4de7 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Thu, 21 Oct 2021 11:02:10 +0000 Subject: [PATCH 19/35] Bug 1160000 - Fix netwerk/test/unit/xpcshell.ini lint warnings r=necko-reviewers,dragana Depends on D129008 Differential Revision: https://phabricator.services.mozilla.com/D129032 --- netwerk/test/unit/xpcshell.ini | 101 +++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 24 deletions(-) diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index be573056e9cae..3861796652002 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -403,7 +403,9 @@ head = head_channels.js head_cache.js head_cookies.js head_trr.js head_http3.js [test_ioservice.js] [test_substituting_protocol_handler.js] [test_proxyconnect.js] -skip-if = tsan || socketprocess_networking # Bug 1614708 +skip-if = + tsan + socketprocess_networking # Bug 1614708 [test_captive_portal_service.js] run-sequentially = node server exceptions dont replay well skip-if = socketprocess_networking @@ -423,16 +425,19 @@ skip-if = os == "android" [test_head_request_no_response_body.js] [test_cache_204_response.js] [test_http3.js] -# asan - bug 1616239 -# tsan - bug 1622845 -# android - bug 1622901 -skip-if = tsan || os =='android' +skip-if = + tsan # bug 1622845 + os =='android' # bug 1622901 run-sequentially = http3server [test_http3_421.js] -skip-if = tsan || os =='android' +skip-if = + tsan + os =='android' run-sequentially = http3server [test_http3_perf.js] -skip-if = tsan || os =='android' +skip-if = + tsan + os =='android' run-sequentially = http3server [test_node_execute.js] [test_loadgroup_cancel.js] @@ -453,9 +458,14 @@ run-sequentially = node server exceptions dont replay well [test_http_sfv.js] [test_blob_channelname.js] [test_altsvc_pref.js] -skip-if = asan || tsan || os =='android' +skip-if = + asan + tsan + os =='android' [test_http3_alt_svc.js] -skip-if = tsan || os =='android' +skip-if = + tsan + os =='android' run-sequentially = http3server [test_use_httpssvc.js] run-sequentially = node server exceptions dont replay well @@ -469,10 +479,16 @@ run-sequentially = node server exceptions dont replay well [test_httpssvc_priority.js] run-sequentially = node server exceptions dont replay well [test_trr_https_fallback.js] -skip-if = asan || tsan || os == 'win' || os =='android' +skip-if = + asan + tsan + os == 'win' + os =='android' run-sequentially = node server exceptions dont replay well [test_http3_trans_close.js] -skip-if = tsan || os =='android' +skip-if = + tsan + os =='android' run-sequentially = http3server [test_brotli_http.js] [test_altsvc_http3.js] @@ -483,18 +499,30 @@ skip-if = os =='android' run-sequentially = http3server [test_http3_fatal_stream_error.js] -skip-if = tsan || os =='android' +skip-if = + tsan + os =='android' run-sequentially = node server exceptions dont replay well [test_http3_large_post.js] -skip-if = tsan || os == 'win' || os =='android' +skip-if = + tsan + os == 'win' + os =='android' [test_http3_error_before_connect.js] -skip-if = tsan || os =='android' +skip-if = + tsan + os =='android' run-sequentially = node server exceptions dont replay well [test_http3_server_not_existing.js] -skip-if = tsan || os =='android' +skip-if = + tsan + os =='android' run-sequentially = node server exceptions dont replay well [test_http3_fast_fallback.js] -skip-if = tsan || os == 'win' || os =='android' +skip-if = + tsan + os == 'win' + os =='android' run-sequentially = node server exceptions dont replay well [test_cookie_ipv6.js] [test_httpssvc_retry_with_ech.js] @@ -506,7 +534,9 @@ skip-if = tsan run-sequentially = node server exceptions dont replay well [test_httpssvc_https_upgrade.js] [test_bug1683176.js] -skip-if = os == "android" || !debug +skip-if = + os == "android" + !debug [test_SuperfluousAuth.js] [test_odoh.js] head = head_channels.js head_cache.js head_cookies.js head_trr.js head_http3.js trr_common.js @@ -523,23 +553,46 @@ skip-if = socketprocess_networking # server on a local ipv6 is not started on mac run-sequentially = node server exceptions dont replay well [test_http3_version1.js] -skip-if = tsan || os == 'win' || os =='android' +skip-if = + tsan + os == 'win' + os =='android' [test_trr_domain.js] [test_http3_progress.js] -skip-if = tsan || os == 'win' || os =='android' +skip-if = + tsan + os == 'win' + os =='android' [test_http3_0rtt.js] -skip-if = tsan || os == 'win' || os =='android' +skip-if = + tsan + os == 'win' + os =='android' [test_http3_large_post_telemetry.js] -skip-if = asan || tsan || os == 'win' || os =='android' || socketprocess_networking +skip-if = + asan + tsan + os == 'win' + os =='android' + socketprocess_networking [test_http3_coalescing.js] -skip-if = tsan || os =='android' || socketprocess_networking +skip-if = + tsan + os =='android' + socketprocess_networking run-sequentially = node server exceptions dont replay well [test_websocket_with_h3_active.js] -skip-if = tsan || os =='android' || (verify && (os == 'win')) || socketprocess_networking +skip-if = + tsan + os =='android' + verify && (os == 'win') + socketprocess_networking run-sequentially = node server exceptions dont replay well [test_304_headers.js] [test_http3_direct_proxy.js] -skip-if = tsan || os =='android' +skip-if = + tsan + os =='android' run-sequentially = node server exceptions dont replay well [test_bug1725766.js] skip-if = os == "android" # skip because of bug 1589327 From c4b44c8a7dc6bc015b39406522f4aab6bcaf0250 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Thu, 21 Oct 2021 11:03:35 +0000 Subject: [PATCH 20/35] Bug 1694085 - Smooth the FFT analysis to make the test more robust. Differential Revision: https://phabricator.services.mozilla.com/D129145 --- dom/media/mediasource/test/test_HEAAC_extradata.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dom/media/mediasource/test/test_HEAAC_extradata.html b/dom/media/mediasource/test/test_HEAAC_extradata.html index 48cda9fe7d365..267c4ff2e637a 100644 --- a/dom/media/mediasource/test/test_HEAAC_extradata.html +++ b/dom/media/mediasource/test/test_HEAAC_extradata.html @@ -33,7 +33,6 @@ await ac.resume(); const mediaElementSource = ac.createMediaElementSource(element); const analyser = new AnalyserNode(ac); - analyser.smoothingTimeConstant = 0.0; // Undo the volume scaling applied globally during test. This is fine because // the audio isn't routed to an actual audio output device in this test, it's @@ -55,7 +54,7 @@ analyser.getFloatFrequencyData(spectrum); const indexFor15kHz = binIndexForFrequency(15000, analyser.fftSize, ac.sampleRate); - ok(spectrum[indexFor15kHz] > -50, + ok(spectrum[indexFor15kHz] > -60, `Energy present at 15kHz (bin index: ${indexFor15kHz}) when playing white noise encoded in HE-AAC ${spectrum[indexFor15kHz]}`); } From ef5149b2d77f993f3a071c8f1b28c8fd9df1e6e0 Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Thu, 21 Oct 2021 11:34:54 +0000 Subject: [PATCH 21/35] Bug 1690731 - Move Places telemetry startup to a scheduled task to ensure it runs after hanlding of user events has started. r=mak Differential Revision: https://phabricator.services.mozilla.com/D128711 --- browser/components/BrowserGlue.jsm | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index 4772993ae9216..f5377170c5d24 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -1241,9 +1241,6 @@ BrowserGlue.prototype = { if (AppConstants.platform == "win") { JawsScreenReaderVersionCheck.init(); } - - // This value is to limit collecting Places telemetry once per session. - this._placesTelemetryGathered = false; }, // cleanup (called on application shutdown) @@ -1697,14 +1694,6 @@ BrowserGlue.prototype = { this._collectStartupConditionsTelemetry(); - if (!this._placesTelemetryGathered && TelemetryUtils.isTelemetryEnabled) { - this._placesTelemetryGathered = true; - // Collect Places telemetry on the first idle. - Services.tm.idleDispatchToMainThread(() => { - PlacesDBUtils.telemetry(); - }); - } - // Set the default favicon size for UI views that use the page-icon protocol. PlacesUtils.favicons.setDefaultIconURIPreferredSize( 16 * aWindow.devicePixelRatio @@ -2345,6 +2334,13 @@ BrowserGlue.prototype = { }, }, + { + condition: TelemetryUtils.isTelemetryEnabled, + task: () => { + PlacesDBUtils.telemetry().catch(console.error); + }, + }, + // Begin listening for incoming push messages. { task: () => { From 03bea7fc78f6a5fd6f6f3c2d4807d6cfc0c09aa6 Mon Sep 17 00:00:00 2001 From: Luca Greco Date: Thu, 21 Oct 2021 11:51:05 +0000 Subject: [PATCH 22/35] Bug 1736825 - Improve manifest.json files parsing in browser_all_files_referenced.js. r=florian Differential Revision: https://phabricator.services.mozilla.com/D129019 --- .../test/static/browser_all_files_referenced.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/browser/base/content/test/static/browser_all_files_referenced.js b/browser/base/content/test/static/browser_all_files_referenced.js index c2def1948e1e4..ef768d81b6f5f 100644 --- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -467,6 +467,12 @@ async function parseJsonManifest(uri) { return uri; } + if (data.background?.scripts) { + for (let bgscript of data.background.scripts) { + gReferencesFromCode.set(uri.resolve(bgscript), null); + } + } + if (data.icons) { for (let icon of Object.values(data.icons)) { gReferencesFromCode.set(uri.resolve(icon), null); @@ -479,6 +485,10 @@ async function parseJsonManifest(uri) { let script = uri.resolve(api.parent.script); gReferencesFromCode.set(script, null); } + + if (api.schema) { + gReferencesFromCode.set(uri.resolve(api.schema), null); + } } } From 0b6fe698903a365161713918634e6589a9543336 Mon Sep 17 00:00:00 2001 From: William Durand Date: Thu, 21 Oct 2021 11:51:06 +0000 Subject: [PATCH 23/35] Bug 1735721 - Move addons-search-detection in tree. r=rpl,florian This patch moves the addons-search-detection system add-on in tree and adjusts the build configuration to make it a built-in add-on (which requires changes in the `BrowserGlue.jsm` file). Summary of the changes made to the different files: - `api.js`: added license header, reformatted with Prettier, then fixed ESLint errors: use of `Services.eTLD` instead of `XPCOMUtils.defineLazyServiceGetter(...)` and defined `"searchInitialized"` on `this` instead of `global` which was unknown - `background.js`: added license header, reformatted with Prettier, removed debug logs because they were off by default anyway, fixed ESLint error: id => addonId because addonId was already defined in a parent scope - `manifest.json`: version number changed + `hidden: true` - `schema.json`: no change await addon.enable(); Depends on D129019 Differential Revision: https://phabricator.services.mozilla.com/D128908 --- .../performance/browser_startup_syncIPC.js | 7 + browser/components/BrowserGlue.jsm | 21 ++ browser/extensions/moz.build | 1 + .../search-detection/extension/api.js | 255 ++++++++++++++++++ .../search-detection/extension/background.js | 177 ++++++++++++ .../search-detection/extension/manifest.json | 32 +++ .../search-detection/extension/schema.json | 60 +++++ browser/extensions/search-detection/jar.mn | 7 + browser/extensions/search-detection/moz.build | 10 + .../tests/browser/browser.ini | 3 + .../tests/browser/browser_extension_loaded.js | 20 ++ 11 files changed, 593 insertions(+) create mode 100644 browser/extensions/search-detection/extension/api.js create mode 100644 browser/extensions/search-detection/extension/background.js create mode 100644 browser/extensions/search-detection/extension/manifest.json create mode 100644 browser/extensions/search-detection/extension/schema.json create mode 100644 browser/extensions/search-detection/jar.mn create mode 100644 browser/extensions/search-detection/moz.build create mode 100644 browser/extensions/search-detection/tests/browser/browser.ini create mode 100644 browser/extensions/search-detection/tests/browser/browser_extension_loaded.js diff --git a/browser/base/content/test/performance/browser_startup_syncIPC.js b/browser/base/content/test/performance/browser_startup_syncIPC.js index 18923ab7c4123..86644abc28caf 100644 --- a/browser/base/content/test/performance/browser_startup_syncIPC.js +++ b/browser/base/content/test/performance/browser_startup_syncIPC.js @@ -288,6 +288,13 @@ const startupPhases = { ignoreIfUnused: true, maxCount: 2, }, + // Added for the search-detection built-in add-on. + { + name: "PGPU::Msg_AddLayerTreeIdMapping", + condition: WIN, + ignoreIfUnused: true, + maxCount: 1, + }, ], }; diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index f5377170c5d24..d5494704af564 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -2049,6 +2049,25 @@ BrowserGlue.prototype = { _checkTranslationsPref(); }, + async _setupSearchDetection() { + // There is no pref for this add-on because it shouldn't be disabled. + const ID = "addons-search-detection@mozilla.com"; + + let addon = await AddonManager.getAddonByID(ID); + + // first time install of addon and install on firefox update + addon = + (await AddonManager.maybeInstallBuiltinAddon( + ID, + "2.0.0", + "resource://builtin-addons/search-detection/" + )) || addon; + + if (!addon.isActive) { + addon.enable(); + } + }, + _monitorHTTPSOnlyPref() { const PREF_ENABLED = "dom.security.https_only_mode"; const PREF_WAS_ENABLED = "dom.security.https_only_mode_ever_enabled"; @@ -2281,6 +2300,8 @@ BrowserGlue.prototype = { this._monitorHTTPSOnlyPref(); this._monitorIonPref(); this._monitorIonStudies(); + this._setupSearchDetection(); + if (AppConstants.NIGHTLY_BUILD) { this._monitorTranslationsPref(); } diff --git a/browser/extensions/moz.build b/browser/extensions/moz.build index 26d059a21aef6..42961bd7d8ba5 100644 --- a/browser/extensions/moz.build +++ b/browser/extensions/moz.build @@ -12,6 +12,7 @@ DIRS += [ "report-site-issue", "pictureinpicture", "proxy-failover", + "search-detection", ] if CONFIG["NIGHTLY_BUILD"]: diff --git a/browser/extensions/search-detection/extension/api.js b/browser/extensions/search-detection/extension/api.js new file mode 100644 index 0000000000000..28320798dff14 --- /dev/null +++ b/browser/extensions/search-detection/extension/api.js @@ -0,0 +1,255 @@ +/* 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"; + +/* global ExtensionCommon, ExtensionAPI, Services, XPCOMUtils, ExtensionUtils */ + +const { AddonManager } = ChromeUtils.import( + "resource://gre/modules/AddonManager.jsm" +); +const { WebRequest } = ChromeUtils.import( + "resource://gre/modules/WebRequest.jsm" +); + +XPCOMUtils.defineLazyGlobalGetters(this, ["ChannelWrapper"]); + +XPCOMUtils.defineLazyGetter(this, "searchInitialized", () => { + if (Services.search.isInitialized) { + return Promise.resolve(); + } + + return ExtensionUtils.promiseObserved( + "browser-search-service", + (_, data) => data === "init-complete" + ); +}); + +const SEARCH_TOPIC_ENGINE_MODIFIED = "browser-search-engine-modified"; + +this.addonsSearchDetection = class extends ExtensionAPI { + getAPI(context) { + const { extension } = context; + + // We want to temporarily store the first monitored URLs that have been + // redirected, indexed by request IDs, so that the background script can + // find the add-on IDs to report. + this.firstMatchedUrls = {}; + + return { + addonsSearchDetection: { + // `getMatchPatterns()` returns a map where each key is an URL pattern + // to monitor and its corresponding value is a list of add-on IDs + // (search engines). + // + // Note: We don't return a simple list of URL patterns because the + // background script might want to lookup add-on IDs for a given URL in + // the case of server-side redirects. + async getMatchPatterns() { + const patterns = {}; + + try { + await searchInitialized; + const visibleEngines = await Services.search.getEngines(); + + visibleEngines.forEach(engine => { + const { _extensionID, _urls } = engine; + + if (!_extensionID) { + // OpenSearch engines don't have an extension ID. + return; + } + + _urls + // We only want to collect "search URLs" (and not "suggestion" + // ones for instance). See `URL_TYPE` in `SearchUtils.jsm`. + .filter(({ type }) => type === "text/html") + .forEach(({ template }) => { + // If this is changed, double check the code in the background + // script because `webRequestCancelledHandler` splits patterns + // on `*` to retrieve URL prefixes. + const pattern = template.split("?")[0] + "*"; + + // Multiple search engines could register URL templates that + // would become the same URL pattern as defined above so we + // store a list of extension IDs per URL pattern. + if (!patterns[pattern]) { + patterns[pattern] = []; + } + + // We exclude built-in search engines because we don't need + // to report them. + if ( + !patterns[pattern].includes(_extensionID) && + !_extensionID.endsWith("@search.mozilla.org") + ) { + patterns[pattern].push(_extensionID); + } + }); + }); + } catch (err) { + Cu.reportError(err); + } + + return patterns; + }, + + // `getAddonVersion()` returns the add-on version if it exists. + async getAddonVersion(addonId) { + const addon = await AddonManager.getAddonByID(addonId); + + return addon && addon.version; + }, + + // `getPublicSuffix()` returns the public suffix/Effective TLD Service + // of the given URL. + // See: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIEffectiveTLDService + async getPublicSuffix(url) { + try { + return Services.eTLD.getBaseDomain(Services.io.newURI(url)); + } catch (err) { + Cu.reportError(err); + return null; + } + }, + + // `onSearchEngineModified` is an event that occurs when the list of + // search engines has changed, e.g., a new engine has been added or an + // engine has been removed. + // + // See: https://searchfox.org/mozilla-central/source/toolkit/components/search/SearchUtils.jsm#145-152 + onSearchEngineModified: new ExtensionCommon.EventManager({ + context, + name: "addonsSearchDetection.onSearchEngineModified", + register: fire => { + const onSearchEngineModifiedObserver = ( + aSubject, + aTopic, + aData + ) => { + if ( + aTopic !== SEARCH_TOPIC_ENGINE_MODIFIED || + // We are only interested in these modified types. + !["engine-added", "engine-removed", "engine-changed"].includes( + aData + ) + ) { + return; + } + + fire.async(); + }; + + Services.obs.addObserver( + onSearchEngineModifiedObserver, + SEARCH_TOPIC_ENGINE_MODIFIED + ); + + return () => { + Services.obs.removeObserver( + onSearchEngineModifiedObserver, + SEARCH_TOPIC_ENGINE_MODIFIED + ); + }; + }, + }).api(), + + // `onRedirected` is an event fired after a request has stopped and + // this request has been redirected once or more. The registered + // listeners will received the following properties: + // + // - `addonId`: the add-on ID that redirected the request, if any. + // - `firstUrl`: the first monitored URL of the request that has + // been redirected. + // - `lastUrl`: the last URL loaded for the request, after one or + // more redirects. + onRedirected: new ExtensionCommon.EventManager({ + context, + name: "addonsSearchDetection.onRedirected", + register: (fire, filter) => { + const stopListener = event => { + if (event.type != "stop") { + return; + } + + const wrapper = event.currentTarget; + const { channel, id: requestId } = wrapper; + + // When we detected a redirect, we read the request property, + // hoping to find an add-on ID corresponding to the add-on that + // initiated the redirect. It might not return anything when the + // redirect is a search server-side redirect but it can also be + // caused by an error. + let addonId; + try { + addonId = channel + ?.QueryInterface(Ci.nsIPropertyBag) + ?.getProperty("redirectedByExtension"); + } catch (err) { + Cu.reportError(err); + } + + const firstUrl = this.firstMatchedUrls[requestId]; + // We don't need this entry anymore. + delete this.firstMatchedUrls[requestId]; + + const lastUrl = wrapper.finalURL; + + if (!firstUrl || !lastUrl) { + // Something went wrong but there is nothing we can do at this + // point. + return; + } + + fire.sync({ addonId, firstUrl, lastUrl }); + }; + + const listener = ({ requestId, url, originUrl }) => { + // We exclude requests not originating from the location bar, + // bookmarks and other "system-ish" requests. + if (originUrl !== undefined) { + return; + } + + // Keep the first monitored URL that was redirected for the + // request until the request has stopped. + if (!this.firstMatchedUrls[requestId]) { + this.firstMatchedUrls[requestId] = url; + + const wrapper = ChannelWrapper.getRegisteredChannel( + requestId, + context.extension.policy, + context.xulBrowser.frameLoader.remoteTab + ); + + wrapper.addEventListener("stop", stopListener); + } + }; + + WebRequest.onBeforeRedirect.addListener( + listener, + // filter + { + types: ["main_frame"], + urls: ExtensionUtils.parseMatchPatterns(filter.urls), + }, + // info + [], + // listener details + { + addonId: extension.id, + policy: extension.policy, + blockingAllowed: false, + } + ); + + return () => { + WebRequest.onBeforeRedirect.removeListener(listener); + }; + }, + }).api(), + }, + }; + } +}; diff --git a/browser/extensions/search-detection/extension/background.js b/browser/extensions/search-detection/extension/background.js new file mode 100644 index 0000000000000..342bfa1065e39 --- /dev/null +++ b/browser/extensions/search-detection/extension/background.js @@ -0,0 +1,177 @@ +/* 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"; + +/* global browser */ + +const TELEMETRY_CATEGORY = "addonsSearchDetection"; +// methods +const TELEMETRY_METHOD_ETLD_CHANGE = "etld_change"; +// objects +const TELEMETRY_OBJECT_WEBREQUEST = "webrequest"; +const TELEMETRY_OBJECT_OTHER = "other"; +// values +const TELEMETRY_VALUE_EXTENSION = "extension"; +const TELEMETRY_VALUE_SERVER = "server"; + +class AddonsSearchDetection { + constructor() { + // The key is an URL pattern to monitor and its corresponding value is a + // list of add-on IDs. + this.matchPatterns = {}; + + browser.telemetry.registerEvents(TELEMETRY_CATEGORY, { + [TELEMETRY_METHOD_ETLD_CHANGE]: { + methods: [TELEMETRY_METHOD_ETLD_CHANGE], + objects: [TELEMETRY_OBJECT_WEBREQUEST, TELEMETRY_OBJECT_OTHER], + extra_keys: ["addonId", "addonVersion", "from", "to"], + record_on_release: true, + }, + }); + + this.onRedirectedListener = this.onRedirectedListener.bind(this); + } + + async getMatchPatterns() { + try { + this.matchPatterns = await browser.addonsSearchDetection.getMatchPatterns(); + } catch (err) { + console.error(`failed to retrieve the list of URL patterns: ${err}`); + this.matchPatterns = {}; + } + + return this.matchPatterns; + } + + // When the search service changes the set of engines that are enabled, we + // update our pattern matching in the webrequest listeners (go to the bottom + // of this file for the search service events we listen to). + async monitor() { + // If there is already a listener, remove it so that we can re-add one + // after. This is because we're using the same listener with different URL + // patterns (when the list of search engines changes). + if ( + browser.addonsSearchDetection.onRedirected.hasListener( + this.onRedirectedListener + ) + ) { + browser.addonsSearchDetection.onRedirected.removeListener( + this.onRedirectedListener + ); + } + // If there is already a listener, remove it so that we can re-add one + // after. This is because we're using the same listener with different URL + // patterns (when the list of search engines changes). + if (browser.webRequest.onBeforeRequest.hasListener(this.noOpListener)) { + browser.webRequest.onBeforeRequest.removeListener(this.noOpListener); + } + + // Retrieve the list of URL patterns to monitor with our listener. + // + // Note: search suggestions are system principal requests, so webRequest + // cannot intercept them. + const matchPatterns = await this.getMatchPatterns(); + const patterns = Object.keys(matchPatterns); + + if (patterns.length === 0) { + return; + } + + browser.webRequest.onBeforeRequest.addListener( + this.noOpListener, + { types: ["main_frame"], urls: patterns }, + ["blocking"] + ); + + browser.addonsSearchDetection.onRedirected.addListener( + this.onRedirectedListener, + { urls: patterns } + ); + } + + // This listener is required to force the registration of traceable channels. + noOpListener() { + // Do nothing. + } + + async onRedirectedListener({ addonId, firstUrl, lastUrl }) { + // When we do not have an add-on ID (in the request property bag), we + // likely detected a search server-side redirect. + const maybeServerSideRedirect = !addonId; + + let addonIds = []; + // Search server-side redirects are possible because an extension has + // registered a search engine, which is why we can (hopefully) retrieve the + // add-on ID. + if (maybeServerSideRedirect) { + addonIds = this.getAddonIdsForUrl(firstUrl); + } else if (addonId) { + addonIds = [addonId]; + } + + if (addonIds.length === 0) { + // No add-on ID means there is nothing we can report. + return; + } + + // This is the monitored URL that was first redirected. + const from = await browser.addonsSearchDetection.getPublicSuffix(firstUrl); + // This is the final URL after redirect(s). + const to = await browser.addonsSearchDetection.getPublicSuffix(lastUrl); + + if (from === to) { + // We do not want to report redirects to same public suffixes. However, + // we will report redirects from public suffixes belonging to a same + // entity (.e.g., `example.com` -> `example.fr`). + // + // Known limitation: if a redirect chain starts and ends with the same + // public suffix, we won't report any event, even if the chain contains + // different public suffixes in between. + return; + } + + const telemetryObject = maybeServerSideRedirect + ? TELEMETRY_OBJECT_OTHER + : TELEMETRY_OBJECT_WEBREQUEST; + const telemetryValue = maybeServerSideRedirect + ? TELEMETRY_VALUE_SERVER + : TELEMETRY_VALUE_EXTENSION; + + for (const id of addonIds) { + const addonVersion = await browser.addonsSearchDetection.getAddonVersion( + id + ); + const extra = { addonId: id, addonVersion, from, to }; + + browser.telemetry.recordEvent( + TELEMETRY_CATEGORY, + TELEMETRY_METHOD_ETLD_CHANGE, + telemetryObject, + telemetryValue, + extra + ); + } + } + + getAddonIdsForUrl(url) { + for (const pattern of Object.keys(this.matchPatterns)) { + // `getMatchPatterns()` returns the prefix plus "*". + const urlPrefix = pattern.slice(0, -1); + + if (url.startsWith(urlPrefix)) { + return this.matchPatterns[pattern]; + } + } + + return []; + } +} + +const exp = new AddonsSearchDetection(); +exp.monitor(); + +browser.addonsSearchDetection.onSearchEngineModified.addListener(async () => { + await exp.monitor(); +}); diff --git a/browser/extensions/search-detection/extension/manifest.json b/browser/extensions/search-detection/extension/manifest.json new file mode 100644 index 0000000000000..91061b4ca37aa --- /dev/null +++ b/browser/extensions/search-detection/extension/manifest.json @@ -0,0 +1,32 @@ +{ + "manifest_version": 2, + "name": "Add-ons Search Detection", + "hidden": true, + "applications": { + "gecko": { + "id": "addons-search-detection@mozilla.com" + } + }, + "version": "2.0.0", + "description": "", + "experiment_apis": { + "addonsSearchDetection": { + "schema": "schema.json", + "parent": { + "scopes": ["addon_parent"], + "script": "api.js", + "events": [], + "paths": [["addonsSearchDetection"]] + } + } + }, + "permissions": [ + "", + "telemetry", + "webRequest", + "webRequestBlocking" + ], + "background": { + "scripts": ["background.js"] + } +} diff --git a/browser/extensions/search-detection/extension/schema.json b/browser/extensions/search-detection/extension/schema.json new file mode 100644 index 0000000000000..e3c77e3f3d7ad --- /dev/null +++ b/browser/extensions/search-detection/extension/schema.json @@ -0,0 +1,60 @@ +[ + { + "namespace": "addonsSearchDetection", + "functions": [ + { + "name": "getMatchPatterns", + "type": "function", + "async": true, + "parameters": [] + }, + { + "name": "getAddonVersion", + "type": "function", + "async": true, + "parameters": [{ "name": "addonId", "type": "string" }] + }, + { + "name": "getPublicSuffix", + "type": "function", + "async": true, + "parameters": [{ "name": "url", "type": "string" }] + } + ], + "events": [ + { + "name": "onSearchEngineModified", + "type": "function", + "parameters": [] + }, + { + "name": "onRedirected", + "type": "function", + "parameters": [ + { + "name": "details", + "type": "object", + "properties": { + "addonId": { "type": "string" }, + "firstUrl": { "type": "string" }, + "lastUrl": { "type": "string" } + } + } + ], + "extraParameters": [ + { + "name": "filter", + "type": "object", + "properties": { + "urls": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1 + } + } + } + ] + } + ] + } +] diff --git a/browser/extensions/search-detection/jar.mn b/browser/extensions/search-detection/jar.mn new file mode 100644 index 0000000000000..377c2be080f79 --- /dev/null +++ b/browser/extensions/search-detection/jar.mn @@ -0,0 +1,7 @@ +# 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/. + +browser.jar: +% resource builtin-addons %builtin-addons/ contentaccessible=yes + builtin-addons/search-detection/ (extension/**) diff --git a/browser/extensions/search-detection/moz.build b/browser/extensions/search-detection/moz.build new file mode 100644 index 0000000000000..7aa40597b1c84 --- /dev/null +++ b/browser/extensions/search-detection/moz.build @@ -0,0 +1,10 @@ +# 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/. + +JAR_MANIFESTS += ["jar.mn"] + +BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.ini"] + +with Files("**"): + BUG_COMPONENT = ("WebExtensions", "General") diff --git a/browser/extensions/search-detection/tests/browser/browser.ini b/browser/extensions/search-detection/tests/browser/browser.ini new file mode 100644 index 0000000000000..b18258e0e6eaf --- /dev/null +++ b/browser/extensions/search-detection/tests/browser/browser.ini @@ -0,0 +1,3 @@ +[DEFAULT] + +[browser_extension_loaded.js] diff --git a/browser/extensions/search-detection/tests/browser/browser_extension_loaded.js b/browser/extensions/search-detection/tests/browser/browser_extension_loaded.js new file mode 100644 index 0000000000000..c8c5beffa9cb7 --- /dev/null +++ b/browser/extensions/search-detection/tests/browser/browser_extension_loaded.js @@ -0,0 +1,20 @@ +/* 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"; + +const { AddonManager } = ChromeUtils.import( + "resource://gre/modules/AddonManager.jsm" +); + +add_task(async function test_searchDetection_isActive() { + let addon = await AddonManager.getAddonByID( + "addons-search-detection@mozilla.com" + ); + + ok(addon, "Add-on exists"); + ok(addon.isActive, "Add-on is active"); + ok(addon.isBuiltin, "Add-on is built-in"); + ok(addon.hidden, "Add-on is hidden"); +}); From 55a8b2e138839e58430483b9e6cc23f04c180048 Mon Sep 17 00:00:00 2001 From: Jens Stutte Date: Thu, 21 Oct 2021 11:51:08 +0000 Subject: [PATCH 24/35] Bug 1735129: Add thread-safe name getter to nsThread. r=xpcom-reviewers,nika Differential Revision: https://phabricator.services.mozilla.com/D128356 --- xpcom/threads/nsThread.cpp | 14 ++++++++++++++ xpcom/threads/nsThread.h | 11 +++++++++++ xpcom/threads/nsThreadUtils.cpp | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 871b9550022bc..48cc7477d1848 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -529,6 +529,7 @@ nsThread::nsThread(NotNull aQueue, new ThreadEventTarget(mEvents.get(), aMainThread == MAIN_THREAD)), mShutdownContext(nullptr), mScriptObserver(nullptr), + mThreadName(""), mStackSize(aStackSize), mNestedEventLoopDepth(0), mShutdownRequired(false), @@ -552,6 +553,7 @@ nsThread::nsThread() mEventTarget(nullptr), mShutdownContext(nullptr), mScriptObserver(nullptr), + mThreadName(""), mStackSize(0), mNestedEventLoopDepth(0), mShutdownRequired(false), @@ -592,6 +594,8 @@ nsresult nsThread::Init(const nsACString& aName) { NS_ADDREF_THIS(); + SetThreadNameInternal(aName); + mShutdownRequired = true; UniquePtr initData( @@ -628,6 +632,16 @@ nsresult nsThread::InitCurrentThread() { return NS_OK; } +void nsThread::GetThreadName(nsACString& aNameBuffer) { + auto lock = mThreadName.Lock(); + aNameBuffer = lock.ref(); +} + +void nsThread::SetThreadNameInternal(const nsACString& aName) { + auto lock = mThreadName.Lock(); + lock->Assign(aName); +} + //----------------------------------------------------------------------------- // nsIEventTarget diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index dc36069699716..04a5cdc0e0ace 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -11,6 +11,7 @@ #include "mozilla/AlreadyAddRefed.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" +#include "mozilla/DataMutex.h" #include "mozilla/EventQueue.h" #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" @@ -183,6 +184,13 @@ class nsThread : public nsIThreadInternal, // Initialize this as a wrapper for the current PRThread. nsresult InitCurrentThread(); + // Get this thread's name, thread-safe. + void GetThreadName(nsACString& aNameBuffer); + + // Set this thread's name. Consider using + // NS_SetCurrentThreadName if you are not sure. + void SetThreadNameInternal(const nsACString& aName); + private: // Initializes the mThreadId and stack base/size members, and adds the thread // to the ThreadList(). @@ -321,6 +329,9 @@ class nsThread : public nsIThreadInternal, mozilla::CycleCollectedJSContext* mScriptObserver; + // Our name. + mozilla::DataMutex mThreadName; + void* mStackBase = nullptr; uint32_t mStackSize; uint32_t mThreadId; diff --git a/xpcom/threads/nsThreadUtils.cpp b/xpcom/threads/nsThreadUtils.cpp index dc6f49d51c44e..1f57347ef9c73 100644 --- a/xpcom/threads/nsThreadUtils.cpp +++ b/xpcom/threads/nsThreadUtils.cpp @@ -475,6 +475,10 @@ void NS_SetCurrentThreadName(const char* aName) { #else PR_SetCurrentThreadName(aName); #endif + if (nsThreadManager::get().IsNSThread()) { + nsThread* thread = nsThreadManager::get().GetCurrentThread(); + thread->SetThreadNameInternal(nsDependentCString(aName)); + } CrashReporter::SetCurrentThreadName(aName); } From 40e243359ce57ac77e8debb71e39b0290467073e Mon Sep 17 00:00:00 2001 From: Jens Stutte Date: Thu, 21 Oct 2021 11:51:09 +0000 Subject: [PATCH 25/35] Bug 1735284: Add a check in nsThread::ShutdownInternal if the shutdown event could be dispatched. r=xpcom-reviewers,nika Differential Revision: https://phabricator.services.mozilla.com/D128167 --- xpcom/threads/nsThread.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 48cc7477d1848..282f52ec824c3 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -792,8 +792,13 @@ nsThreadShutdownContext* nsThread::ShutdownInternal(bool aSync) { // events to process. nsCOMPtr event = new nsThreadShutdownEvent(WrapNotNull(this), WrapNotNull(context)); - // XXXroc What if posting the event fails due to OOM? - mEvents->PutEvent(event.forget(), EventQueuePriority::Normal); + if (!mEvents->PutEvent(event.forget(), EventQueuePriority::Normal)) { + // We do not expect this to happen. Let's collect some diagnostics. + nsAutoCString threadName; + currentThread->GetThreadName(threadName); + MOZ_CRASH_UNSAFE_PRINTF("Attempt to shutdown an already dead thread: %s", + threadName.get()); + } // We could still end up with other events being added after the shutdown // task, but that's okay because we process pending events in ThreadFunc From cf47bf9cbf99a74dd86f250d9ba43b458ab165f8 Mon Sep 17 00:00:00 2001 From: Jon Barson Date: Thu, 21 Oct 2021 11:56:35 +0000 Subject: [PATCH 26/35] Bug 1632476 changed hostname for media permissions to include the port r=johannh Differential Revision: https://phabricator.services.mozilla.com/D128109 --- browser/modules/webrtcUI.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/modules/webrtcUI.jsm b/browser/modules/webrtcUI.jsm index 63205747c9bf9..816a29bf16fc3 100644 --- a/browser/modules/webrtcUI.jsm +++ b/browser/modules/webrtcUI.jsm @@ -782,7 +782,7 @@ var webrtcUI = { } let addonPolicy = WebExtensionPolicy.getByURI(uri); - host = addonPolicy ? addonPolicy.name : uri.host; + host = addonPolicy?.name ?? uri.hostPort; } catch (ex) {} if (!host) { From edc33dea572a671a9c27edf0ecd9d5b5d88a1cdd Mon Sep 17 00:00:00 2001 From: Jens Stutte Date: Thu, 21 Oct 2021 12:46:06 +0000 Subject: [PATCH 27/35] Bug 1735129: Add thread's name in nsThread::Shutdown's SpinEventLoopUntil annotation. r=xpcom-reviewers,nika Differential Revision: https://phabricator.services.mozilla.com/D128270 --- xpcom/threads/nsThread.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 282f52ec824c3..e6c5a28d371d2 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -855,10 +855,14 @@ nsThread::Shutdown() { NotNull context = WrapNotNull(maybeContext); + // If we are going to hang here we want to see the thread's name + nsAutoCString threadName; + GetThreadName(threadName); + // Process events on the current thread until we receive a shutdown ACK. // Allows waiting; ensure no locks are held that would deadlock us! SpinEventLoopUntil( - "nsThread::Shutdown"_ns, + "nsThread::Shutdown: "_ns + threadName, [&, context]() { return !context->mAwaitingShutdownAck; }, context->mJoiningThread); From 212af2f1b20dafbe4ae3af9dcf45e47b5e2193a1 Mon Sep 17 00:00:00 2001 From: Myeongjun Go Date: Thu, 21 Oct 2021 12:47:16 +0000 Subject: [PATCH 28/35] Bug 1635183 - [mozperftest] Port browsertime log parsing to mozperftest r=sparky Differential Revision: https://phabricator.services.mozilla.com/D120242 --- .../mozperftest/test/browsertime/runner.py | 20 +++++++++++++++++-- .../mozperftest/test/noderunner.py | 5 +++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/python/mozperftest/mozperftest/test/browsertime/runner.py b/python/mozperftest/mozperftest/test/browsertime/runner.py index bd02fe43c7408..2935c86eb97b4 100644 --- a/python/mozperftest/mozperftest/test/browsertime/runner.py +++ b/python/mozperftest/mozperftest/test/browsertime/runner.py @@ -314,6 +314,22 @@ def _android_args(self, metadata): return args_list + def _line_handler(self, line): + line_matcher = re.compile(r"(\[\d{4}-\d{2}-\d{2}.*\])\s+([a-zA-Z]+):\s+(.*)") + match = line_matcher.match(line) + if not match: + return + + date, level, msg = match.groups() + msg = msg.replace("{", "{{").replace("}", "}}") + level = level.lower() + if "error" in level: + self.error("Mozperftest failed to run: ", msg) + elif "warning" in level: + self.warning(msg) + else: + self.info(msg) + def run(self, metadata): self._test_script = metadata.script self.setup() @@ -397,9 +413,9 @@ def _one_cycle(self, metadata, result_dir): if visualmetrics and self.get_arg("xvfb"): with xvfb(): - exit_code = self.node(command) + exit_code = self.node(command, self._line_handler) else: - exit_code = self.node(command) + exit_code = self.node(command, self._line_handler) if exit_code != 0: raise NodeException(exit_code) diff --git a/python/mozperftest/mozperftest/test/noderunner.py b/python/mozperftest/mozperftest/test/noderunner.py index 0422817a467b8..4304609bff957 100644 --- a/python/mozperftest/mozperftest/test/noderunner.py +++ b/python/mozperftest/mozperftest/test/noderunner.py @@ -27,14 +27,15 @@ def setup(self): """Install the Node.js package.""" self.verify_node_install() - def node(self, args): + def node(self, args, line_handler=None): """Invoke node (interactively) with the given arguments.""" return self.run_process( [self.node_path] + args, append_env=self.append_env(), - pass_thru=True, # Allow user to run Node interactively. + pass_thru=False, # Allow user to run Node interactively. ensure_exit_code=False, # Don't throw on non-zero exit code. cwd=mozpath.join(self.topsrcdir), + line_handler=line_handler, ) def append_env(self, append_path=True): From 10f658ae6dc86a651ea3ce506c935025dd936ae5 Mon Sep 17 00:00:00 2001 From: Norisz Fay Date: Thu, 21 Oct 2021 16:36:47 +0300 Subject: [PATCH 29/35] Backed out 3 changesets (bug 1160000) for causing xpcshell failures on test_ntlm_web_auth.js Backed out changeset b44e97e2ca07 (bug 1160000) Backed out changeset 4568eec2f406 (bug 1160000) Backed out changeset b168979602ed (bug 1160000) --- .../test/unit/test_ntlm_proxy_and_web_auth.js | 354 ----------------- netwerk/test/unit/test_ntlm_proxy_auth.js | 357 ------------------ netwerk/test/unit/test_ntlm_web_auth.js | 247 ------------ netwerk/test/unit/xpcshell.ini | 104 ++--- 4 files changed, 24 insertions(+), 1038 deletions(-) delete mode 100644 netwerk/test/unit/test_ntlm_proxy_and_web_auth.js delete mode 100644 netwerk/test/unit/test_ntlm_proxy_auth.js delete mode 100644 netwerk/test/unit/test_ntlm_web_auth.js diff --git a/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js b/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js deleted file mode 100644 index 87e92678ce9db..0000000000000 --- a/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js +++ /dev/null @@ -1,354 +0,0 @@ -// Unit tests for a NTLM authenticated proxy, proxying for a NTLM authenticated -// web server. -// -// Currently the tests do not determine whether the Authentication dialogs have -// been displayed. -// - -"use strict"; - -const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); -ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); - -XPCOMUtils.defineLazyGetter(this, "URL", function() { - return "http://localhost:" + httpserver.identity.primaryPort; -}); - -function AuthPrompt() {} - -AuthPrompt.prototype = { - user: "guest", - pass: "guest", - - QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]), - - promptAuth: function ap_promptAuth(channel, level, authInfo) { - authInfo.username = this.user; - authInfo.password = this.pass; - - return true; - }, - - asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { - throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); - }, -}; - -function Requestor() {} - -Requestor.prototype = { - QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]), - - getInterface: function requestor_gi(iid) { - if (iid.equals(Ci.nsIAuthPrompt2)) { - // Allow the prompt to store state by caching it here - if (!this.prompt) { - this.prompt = new AuthPrompt(); - } - return this.prompt; - } - - throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); - }, - - prompt: null, -}; - -function makeChan(url, loadingUrl) { - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].getService( - Ci.nsIScriptSecurityManager - ); - var principal = ssm.createContentPrincipal(ios.newURI(loadingUrl), {}); - return NetUtil.newChannel({ - uri: url, - loadingPrincipal: principal, - securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, - contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, - }); -} - -function TestListener(resolve) { - this.resolve = resolve; -} -TestListener.prototype.onStartRequest = function(request, context) { - // Need to do the instanceof to allow request.responseStatus - // to be read. - if (!(request instanceof Ci.nsIHttpChannel)) { - dump("Expecting an HTTP channel"); - } - - Assert.equal(expectedResponse, request.responseStatus, "HTTP Status code"); -}; -TestListener.prototype.onStopRequest = function(request, context, status) { - Assert.equal(expectedRequests, requestsMade, "Number of requests made "); - - this.resolve(); -}; -TestListener.prototype.onDataAvaiable = function( - request, - context, - stream, - offset, - count -) { - read_stream(stream, count); -}; - -// NTLM Messages, for the received type 1 and 3 messages only check that they -// are of the expected type. -const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; -const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; -const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; -const NTLM_PREFIX_LEN = 21; - -const NTLM_CHALLENGE = - NTLM_TYPE2_PREFIX + - "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" + - "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" + - "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" + - "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="; - -const PROXY_CHALLENGE = - NTLM_TYPE2_PREFIX + - "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" + - "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" + - "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" + - "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA"; - -// Proxy and Web server responses for the happy path scenario. -// i.e. successful proxy auth and successful web server auth -// -function authHandler(metadata, response) { - switch (requestsMade) { - case 0: - // Proxy - First request to the Proxy resppond with a 407 to start auth - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); - break; - case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Will respond with a 401 to start web server auth sequence - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", "NTLM", false); - break; - case 3: - // Web Server - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false); - break; - case 4: - // Web Server - Expecting a type 3 Authenticate message from the client - var authorization = metadata.getHeader("Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine(metadata.httpVersion, 200, "Successful"); - break; - default: - // We should be authenticated and further requests are permitted - var authorization = metadata.getHeader("Authorization"); - var authorization = metadata.getHeader("Proxy-Authorization"); - Assert.isnull(authorization); - response.setStatusLine(metadata.httpVersion, 200, "Successful"); - } - requestsMade++; -} - -// Proxy responses simulating an invalid proxy password -// Note: that the connection should not be reused after the -// proxy auth fails. -// -function authHandlerInvalidProxyPassword(metadata, response) { - switch (requestsMade) { - case 0: - // Proxy - First request respond with a 407 to initiate auth sequence - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); - break; - case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Respond with a 407 to indicate invalid credentials - // - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - default: - // Strictly speaking the connection should not be reused at this point - // and reaching here should be an error, but have commented out for now - //dump( "ERROR: NTLM Proxy Authentication, connection should not be reused"); - //Assert.fail(); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - } - requestsMade++; -} - -// Proxy and web server responses simulating a successful Proxy auth -// and a failed web server auth -// Note: the connection should not be reused once the password failure is -// detected -function authHandlerInvalidWebPassword(metadata, response) { - switch (requestsMade) { - case 0: - // Proxy - First request return a 407 to start Proxy auth - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", NTLM_CHALLENGE, false); - break; - case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Responds with a 401 to start web server auth - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", "NTLM", false); - break; - case 3: - // Web Server - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false); - break; - case 4: - // Web Server - Expecting a type 3 Authenticate message from the client - // Respond with a 401 to restart the auth sequence. - var authorization = metadata.getHeader("Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - break; - default: - // We should not get called past step 4 - dump("ERROR: NTLM Auth failed connection should not be reused"); - Assert.fail(); - } - requestsMade++; -} - -// Tests to run test_bad_proxy_pass and test_bad_web_pass are split into two stages -// so that once we determine how detect password dialog displays we can check -// that the are displayed correctly, i.e. proxy password should not be prompted -// for when retrying the web server password - -var httpserver = null; -function setup() { - httpserver = new HttpServer(); - httpserver.start(-1); - - Services.prefs.setCharPref("network.proxy.http", "localhost"); - Services.prefs.setIntPref( - "network.proxy.http_port", - httpserver.identity.primaryPort - ); - Services.prefs.setCharPref("network.proxy.no_proxies_on", ""); - Services.prefs.setIntPref("network.proxy.type", 1); - Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); - - registerCleanupFunction(async () => { - Services.prefs.clearUserPref("network.proxy.http"); - Services.prefs.clearUserPref("network.proxy.http_port"); - Services.prefs.clearUserPref("network.proxy.no_proxies_on"); - Services.prefs.clearUserPref("network.proxy.type"); - Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); - - await httpserver.stop(); - }); -} -setup(); - -var expectedRequests = 0; // Number of HTTP requests that are expected -var requestsMade = 0; // The number of requests that were made -var expectedResponse = 0; // The response code -// Note that any test failures in the HTTP handler -// will manifest as a 500 response code - -// Common test setup -// Parameters: -// path - path component of the URL -// handler - http handler function for the httpserver -// requests - expected number oh http requests -// response - expected http response code -// clearCache - clear the authentication cache before running the test -function setupTest(path, handler, requests, response, clearCache) { - requestsMade = 0; - expectedRequests = requests; - expectedResponse = response; - - // clear the auth cache if requested - if (clearCache) { - dump("Clearing auth cache"); - Cc["@mozilla.org/network/http-auth-manager;1"] - .getService(Ci.nsIHttpAuthManager) - .clearAll(); - } - - return new Promise(resolve => { - var chan = makeChan(URL + path, URL); - httpserver.registerPathHandler(path, handler); - chan.notificationCallbacks = new Requestor(); - chan.asyncOpen(new TestListener(resolve)); - }); -} - -// Happy code path -// Succesful proxy and web server auth. -add_task(async function test_happy_path() { - dump("RUNNING TEST: test_happy_path"); - await setupTest("/auth", authHandler, 5, 200, 1); -}); - -// Failed proxy authentication -add_task(async function test_bad_proxy_pass_stage01() { - dump("RUNNING TEST: test_bad_proxy_pass_stage01"); - await setupTest("/auth", authHandlerInvalidProxyPassword, 4, 407, 1); -}); -// Successful logon after failed proxy auth -add_task(async function test_bad_proxy_pass_stage02() { - dump("RUNNING TEST: test_bad_proxy_pass_stage02"); - await setupTest("/auth", authHandler, 5, 200, 0); -}); - -// successful proxy logon, unsuccessful web server sign on -add_task(async function test_bad_web_pass_stage01() { - dump("RUNNING TEST: test_bad_web_pass_stage01"); - await setupTest("/auth", authHandlerInvalidWebPassword, 5, 401, 1); -}); -// successful logon after failed web server auth. -add_task(async function test_bad_web_pass_stage02() { - dump("RUNNING TEST: test_bad_web_pass_stage02"); - await setupTest("/auth", authHandler, 5, 200, 0); -}); diff --git a/netwerk/test/unit/test_ntlm_proxy_auth.js b/netwerk/test/unit/test_ntlm_proxy_auth.js deleted file mode 100644 index ee8ff1cd868b2..0000000000000 --- a/netwerk/test/unit/test_ntlm_proxy_auth.js +++ /dev/null @@ -1,357 +0,0 @@ -// Unit tests for a NTLM authenticated proxy -// -// Currently the tests do not determine whether the Authentication dialogs have -// been displayed. -// - -"use strict"; - -const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); -ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); - -XPCOMUtils.defineLazyGetter(this, "URL", function() { - return "http://localhost:" + httpserver.identity.primaryPort; -}); - -function AuthPrompt() {} - -AuthPrompt.prototype = { - user: "guest", - pass: "guest", - - QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]), - - promptAuth: function ap_promptAuth(channel, level, authInfo) { - authInfo.username = this.user; - authInfo.password = this.pass; - - return true; - }, - - asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { - throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); - }, -}; - -function Requestor() {} - -Requestor.prototype = { - QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]), - - getInterface: function requestor_gi(iid) { - if (iid.equals(Ci.nsIAuthPrompt2)) { - // Allow the prompt to store state by caching it here - if (!this.prompt) { - this.prompt = new AuthPrompt(); - } - return this.prompt; - } - - throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); - }, - - prompt: null, -}; - -function makeChan(url, loadingUrl) { - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].getService( - Ci.nsIScriptSecurityManager - ); - var principal = ssm.createContentPrincipal(ios.newURI(loadingUrl), {}); - return NetUtil.newChannel({ - uri: url, - loadingPrincipal: principal, - securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, - contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, - }); -} - -function TestListener(resolve) { - this.resolve = resolve; -} -TestListener.prototype.onStartRequest = function(request, context) { - // Need to do the instanceof to allow request.responseStatus - // to be read. - if (!(request instanceof Ci.nsIHttpChannel)) { - dump("Expecting an HTTP channel"); - } - - Assert.equal(expectedResponse, request.responseStatus, "HTTP Status code"); -}; -TestListener.prototype.onStopRequest = function(request, context, status) { - Assert.equal(expectedRequests, requestsMade, "Number of requests made "); - Assert.equal( - exptTypeOneCount, - ntlmTypeOneCount, - "Number of type one messages received" - ); - Assert.equal( - exptTypeTwoCount, - ntlmTypeTwoCount, - "Number of type two messages received" - ); - - this.resolve(); -}; -TestListener.prototype.onDataAvaiable = function( - request, - context, - stream, - offset, - count -) { - read_stream(stream, count); -}; - -// NTLM Messages, for the received type 1 and 3 messages only check that they -// are of the expected type. -const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; -const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; -const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; -const NTLM_PREFIX_LEN = 21; - -const PROXY_CHALLENGE = - NTLM_TYPE2_PREFIX + - "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" + - "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" + - "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" + - "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA"; - -// Proxy responses for the happy path scenario. -// i.e. successful proxy auth -// -function successfulAuth(metadata, response) { - switch (requestsMade) { - case 0: - // Proxy - First request to the Proxy resppond with a 407 to start auth - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); - break; - case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Will respond with a 401 to start web server auth sequence - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine(metadata.httpVersion, 200, "Successful"); - break; - default: - // We should be authenticated and further requests are permitted - var authorization = metadata.getHeader("Proxy-Authorization"); - Assert.isnull(authorization); - response.setStatusLine(metadata.httpVersion, 200, "Successful"); - } - requestsMade++; -} - -// Proxy responses simulating an invalid proxy password -// Note: that the connection should not be reused after the -// proxy auth fails. -// -function failedAuth(metadata, response) { - switch (requestsMade) { - case 0: - // Proxy - First request respond with a 407 to initiate auth sequence - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); - break; - case 2: - // Proxy - Expecting a type 3 Authenticate message from the client - // Respond with a 407 to indicate invalid credentials - // - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - default: - // Strictly speaking the connection should not be reused at this point - // commenting out for now. - dump("ERROR: NTLM Proxy Authentication, connection should not be reused"); - // assert.fail(); - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - } - requestsMade++; -} -// -// Simulate a connection reset once the connection has been authenticated -// Detects bug 486508 -// -function connectionReset(metadata, response) { - switch (requestsMade) { - case 0: - // Proxy - First request to the Proxy resppond with a 407 to start auth - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - ntlmTypeOneCount++; - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); - break; - case 2: - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - ntlmTypeTwoCount++; - response.seizePower(); - response.bodyOutPutStream.close(); - response.finish(); - break; - default: - // Should not get any further requests on this channel - dump("ERROR: NTLM Proxy Authentication, connection should not be reused"); - Assert.ok(false); - } - requestsMade++; -} - -// -// Reset the connection after a nogotiate message has been received -// -function connectionReset02(metadata, response) { - switch (requestsMade) { - case 0: - // Proxy - First request to the Proxy resppond with a 407 to start auth - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", "NTLM", false); - break; - case 1: - // Proxy - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Proxy-Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - ntlmTypeOneCount++; - response.setStatusLine(metadata.httpVersion, 407, "Unauthorized"); - response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false); - response.finish(); - response.seizePower(); - response.bodyOutPutStream.close(); - break; - default: - // Should not get any further requests on this channel - dump("ERROR: NTLM Proxy Authentication, connection should not be reused"); - Assert.ok(false); - } - requestsMade++; -} - -var httpserver = null; -function setup() { - httpserver = new HttpServer(); - httpserver.start(-1); - - Services.prefs.setCharPref("network.proxy.http", "localhost"); - Services.prefs.setIntPref( - "network.proxy.http_port", - httpserver.identity.primaryPort - ); - Services.prefs.setCharPref("network.proxy.no_proxies_on", ""); - Services.prefs.setIntPref("network.proxy.type", 1); - Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); - - registerCleanupFunction(async () => { - Services.prefs.clearUserPref("network.proxy.http"); - Services.prefs.clearUserPref("network.proxy.http_port"); - Services.prefs.clearUserPref("network.proxy.no_proxies_on"); - Services.prefs.clearUserPref("network.proxy.type"); - Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); - - await httpserver.stop(); - }); -} -setup(); - -var expectedRequests = 0; // Number of HTTP requests that are expected -var requestsMade = 0; // The number of requests that were made -var expectedResponse = 0; // The response code -// Note that any test failures in the HTTP handler -// will manifest as a 500 response code - -// Common test setup -// Parameters: -// path - path component of the URL -// handler - http handler function for the httpserver -// requests - expected number oh http requests -// response - expected http response code -// clearCache - clear the authentication cache before running the test -function setupTest(path, handler, requests, response, clearCache) { - requestsMade = 0; - expectedRequests = requests; - expectedResponse = response; - - // clear the auth cache if requested - if (clearCache) { - dump("Clearing auth cache"); - Cc["@mozilla.org/network/http-auth-manager;1"] - .getService(Ci.nsIHttpAuthManager) - .clearAll(); - } - - return new Promise(resolve => { - var chan = makeChan(URL + path, URL); - httpserver.registerPathHandler(path, handler); - chan.notificationCallbacks = new Requestor(); - chan.asyncOpen(new TestListener(resolve)); - }); -} - -// Happy code path -// Succesful proxy auth. -add_task(async function test_happy_path() { - dump("RUNNING TEST: test_happy_path"); - await setupTest("/auth", successfulAuth, 3, 200, 1); -}); - -// Failed proxy authentication -add_task(async function test_failed_auth() { - dump("RUNNING TEST:failed auth "); - await setupTest("/auth", failedAuth, 4, 407, 1); -}); - -var ntlmTypeOneCount = 0; // The number of NTLM type one messages received -var exptTypeOneCount = 0; // The number of NTLM type one messages that should be received - -var ntlmTypeTwoCount = 0; // The number of NTLM type two messages received -var exptTypeTwoCount = 0; // The number of NTLM type two messages that should received -// Test connection reset, after successful auth -add_task(async function test_connection_reset() { - dump("RUNNING TEST:connection reset "); - ntlmTypeOneCount = 0; - ntlmTypeTwoCount = 0; - exptTypeOneCount = 1; - exptTypeTwoCount = 1; - await setupTest("/auth", connectionReset, 2, 500, 1); -}); - -// Test connection reset after sending a negotiate. -add_task(async function test_connection_reset02() { - dump("RUNNING TEST:connection reset "); - ntlmTypeOneCount = 0; - ntlmTypeTwoCount = 0; - exptTypeOneCount = 1; - exptTypeTwoCount = 0; - await setupTest("/auth", connectionReset02, 1, 500, 1); -}); diff --git a/netwerk/test/unit/test_ntlm_web_auth.js b/netwerk/test/unit/test_ntlm_web_auth.js deleted file mode 100644 index 61ea61825ef3a..0000000000000 --- a/netwerk/test/unit/test_ntlm_web_auth.js +++ /dev/null @@ -1,247 +0,0 @@ -// Unit tests for a NTLM authenticated web server. -// -// Currently the tests do not determine whether the Authentication dialogs have -// been displayed. -// - -"use strict"; - -const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); -ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); - -XPCOMUtils.defineLazyGetter(this, "URL", function() { - return "http://localhost:" + httpserver.identity.primaryPort; -}); - -function AuthPrompt() {} - -AuthPrompt.prototype = { - user: "guest", - pass: "guest", - - QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]), - - promptAuth: function ap_promptAuth(channel, level, authInfo) { - authInfo.username = this.user; - authInfo.password = this.pass; - - return true; - }, - - asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) { - throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); - }, -}; - -function Requestor() {} - -Requestor.prototype = { - QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]), - - getInterface: function requestor_gi(iid) { - if (iid.equals(Ci.nsIAuthPrompt2)) { - // Allow the prompt to store state by caching it here - if (!this.prompt) { - this.prompt = new AuthPrompt(); - } - return this.prompt; - } - - throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); - }, - - prompt: null, -}; - -function makeChan(url, loadingUrl) { - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var principal = Services.scriptSecurityManager.createContentPrincipal( - ios.newURI(loadingUrl), - {} - ); - return NetUtil.newChannel({ - uri: url, - loadingPrincipal: principal, - securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, - contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, - }); -} - -function TestListener() {} -TestListener.prototype.onStartRequest = function(request, context) { - // Need to do the instanceof to allow request.responseStatus - // to be read. - if (!(request instanceof Ci.nsIHttpChannel)) { - dump("Expecting an HTTP channel"); - } - - Assert.equal(expectedResponse, request.responseStatus, "HTTP Status code"); -}; -TestListener.prototype.onStopRequest = function(request, context, status) { - Assert.equal(expectedRequests, requestsMade, "Number of requests made "); - - if (current_test < tests.length - 1) { - current_test++; - tests[current_test](); - } else { - do_test_pending(); - httpserver.stop(do_test_finished); - } - - do_test_finished(); -}; -TestListener.prototype.onDataAvaiable = function( - request, - context, - stream, - offset, - count -) { - read_stream(stream, count); -}; - -// NTLM Messages, for the received type 1 and 3 messages only check that they -// are of the expected type. -const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA"; -const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA"; -const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA"; -const NTLM_PREFIX_LEN = 21; - -const NTLM_CHALLENGE = - NTLM_TYPE2_PREFIX + - "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" + - "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" + - "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" + - "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="; - -// Web server responses for the happy path scenario. -// i.e. successful web server auth -// -function successfulAuth(metadata, response) { - switch (requestsMade) { - case 0: - // Web Server - Initial request - // Will respond with a 401 to start web server auth sequence - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", "NTLM", false); - break; - case 1: - // Web Server - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false); - break; - case 2: - // Web Server - Expecting a type 3 Authenticate message from the client - var authorization = metadata.getHeader("Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message"); - response.setStatusLine(metadata.httpVersion, 200, "Successful"); - break; - default: - // We should be authenticated and further requests are permitted - var authorization = metadata.getHeader("Authorization"); - Assert.isnull(authorization); - response.setStatusLine(metadata.httpVersion, 200, "Successful"); - } - requestsMade++; -} - -// web server responses simulating an unsuccessful web server auth -function failedAuth(metadata, response) { - switch (requestsMade) { - case 0: - // Web Server - First request return a 401 to start auth sequence - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", "NTLM", false); - break; - case 1: - // Web Server - Expecting a type 1 negotiate message from the client - var authorization = metadata.getHeader("Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false); - break; - case 2: - // Web Server - Expecting a type 3 Authenticate message from the client - // Respond with a 401 to restart the auth sequence. - var authorization = metadata.getHeader("Authorization"); - var authPrefix = authorization.substring(0, NTLM_PREFIX_LEN); - Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message"); - response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); - break; - default: - // We should not get called past step 2 - // Strictly speaking the connection should not be used again - // commented out for testing - // dump( "ERROR: NTLM Auth failed connection should not be reused"); - //Assert.fail(); - response.setHeader("WWW-Authenticate", "NTLM", false); - } - requestsMade++; -} - -var tests = [test_happy_path, test_failed_auth]; -var current_test = 0; - -var httpserver = null; -function run_test() { - httpserver = new HttpServer(); - httpserver.start(-1); - - tests[0](); -} - -var expectedRequests = 0; // Number of HTTP requests that are expected -var requestsMade = 0; // The number of requests that were made -var expectedResponse = 0; // The response code -// Note that any test failures in the HTTP handler -// will manifest as a 500 response code - -// Common test setup -// Parameters: -// path - path component of the URL -// handler - http handler function for the httpserver -// requests - expected number oh http requests -// response - expected http response code -// clearCache - clear the authentication cache before running the test -function setupTest(path, handler, requests, response, clearCache) { - requestsMade = 0; - expectedRequests = requests; - expectedResponse = response; - - // clear the auth cache if requested - if (clearCache) { - dump("Clearing auth cache"); - Cc["@mozilla.org/network/http-auth-manager;1"] - .getService(Ci.nsIHttpAuthManager) - .clearAll(); - } - - var chan = makeChan(URL + path, URL); - httpserver.registerPathHandler(path, handler); - chan.notificationCallbacks = new Requestor(); - chan.asyncOpen(new TestListener()); - - return chan; -} - -// Happy code path -// Succesful web server auth. -function test_happy_path() { - dump("RUNNING TEST: test_happy_path"); - setupTest("/auth", successfulAuth, 3, 200, 1); - - do_test_pending(); -} - -// Unsuccessful web server sign on -function test_failed_auth() { - dump("RUNNING TEST: test_failed_auth"); - setupTest("/auth", failedAuth, 3, 401, 1); - - do_test_pending(); -} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 3861796652002..d70dfd7e0f686 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -380,9 +380,6 @@ skip-if = win10_2004 && bits == 64 # Bug 1718292 [test_throttling.js] [test_separate_connections.js] [test_trackingProtection_annotateChannels.js] -[test_ntlm_web_auth.js] -[test_ntlm_proxy_auth.js] -[test_ntlm_proxy_and_web_auth.js] [test_race_cache_with_network.js] [test_rcwn_always_cache_new_content.js] [test_channel_priority.js] @@ -403,9 +400,7 @@ head = head_channels.js head_cache.js head_cookies.js head_trr.js head_http3.js [test_ioservice.js] [test_substituting_protocol_handler.js] [test_proxyconnect.js] -skip-if = - tsan - socketprocess_networking # Bug 1614708 +skip-if = tsan || socketprocess_networking # Bug 1614708 [test_captive_portal_service.js] run-sequentially = node server exceptions dont replay well skip-if = socketprocess_networking @@ -425,19 +420,16 @@ skip-if = os == "android" [test_head_request_no_response_body.js] [test_cache_204_response.js] [test_http3.js] -skip-if = - tsan # bug 1622845 - os =='android' # bug 1622901 +# asan - bug 1616239 +# tsan - bug 1622845 +# android - bug 1622901 +skip-if = tsan || os =='android' run-sequentially = http3server [test_http3_421.js] -skip-if = - tsan - os =='android' +skip-if = tsan || os =='android' run-sequentially = http3server [test_http3_perf.js] -skip-if = - tsan - os =='android' +skip-if = tsan || os =='android' run-sequentially = http3server [test_node_execute.js] [test_loadgroup_cancel.js] @@ -458,14 +450,9 @@ run-sequentially = node server exceptions dont replay well [test_http_sfv.js] [test_blob_channelname.js] [test_altsvc_pref.js] -skip-if = - asan - tsan - os =='android' +skip-if = asan || tsan || os =='android' [test_http3_alt_svc.js] -skip-if = - tsan - os =='android' +skip-if = tsan || os =='android' run-sequentially = http3server [test_use_httpssvc.js] run-sequentially = node server exceptions dont replay well @@ -479,16 +466,10 @@ run-sequentially = node server exceptions dont replay well [test_httpssvc_priority.js] run-sequentially = node server exceptions dont replay well [test_trr_https_fallback.js] -skip-if = - asan - tsan - os == 'win' - os =='android' +skip-if = asan || tsan || os == 'win' || os =='android' run-sequentially = node server exceptions dont replay well [test_http3_trans_close.js] -skip-if = - tsan - os =='android' +skip-if = tsan || os =='android' run-sequentially = http3server [test_brotli_http.js] [test_altsvc_http3.js] @@ -499,30 +480,18 @@ skip-if = os =='android' run-sequentially = http3server [test_http3_fatal_stream_error.js] -skip-if = - tsan - os =='android' +skip-if = tsan || os =='android' run-sequentially = node server exceptions dont replay well [test_http3_large_post.js] -skip-if = - tsan - os == 'win' - os =='android' +skip-if = tsan || os == 'win' || os =='android' [test_http3_error_before_connect.js] -skip-if = - tsan - os =='android' +skip-if = tsan || os =='android' run-sequentially = node server exceptions dont replay well [test_http3_server_not_existing.js] -skip-if = - tsan - os =='android' +skip-if = tsan || os =='android' run-sequentially = node server exceptions dont replay well [test_http3_fast_fallback.js] -skip-if = - tsan - os == 'win' - os =='android' +skip-if = tsan || os == 'win' || os =='android' run-sequentially = node server exceptions dont replay well [test_cookie_ipv6.js] [test_httpssvc_retry_with_ech.js] @@ -534,9 +503,7 @@ skip-if = tsan run-sequentially = node server exceptions dont replay well [test_httpssvc_https_upgrade.js] [test_bug1683176.js] -skip-if = - os == "android" - !debug +skip-if = os == "android" || !debug [test_SuperfluousAuth.js] [test_odoh.js] head = head_channels.js head_cache.js head_cookies.js head_trr.js head_http3.js trr_common.js @@ -553,46 +520,23 @@ skip-if = socketprocess_networking # server on a local ipv6 is not started on mac run-sequentially = node server exceptions dont replay well [test_http3_version1.js] -skip-if = - tsan - os == 'win' - os =='android' +skip-if = tsan || os == 'win' || os =='android' [test_trr_domain.js] [test_http3_progress.js] -skip-if = - tsan - os == 'win' - os =='android' +skip-if = tsan || os == 'win' || os =='android' [test_http3_0rtt.js] -skip-if = - tsan - os == 'win' - os =='android' +skip-if = tsan || os == 'win' || os =='android' [test_http3_large_post_telemetry.js] -skip-if = - asan - tsan - os == 'win' - os =='android' - socketprocess_networking +skip-if = asan || tsan || os == 'win' || os =='android' || socketprocess_networking [test_http3_coalescing.js] -skip-if = - tsan - os =='android' - socketprocess_networking +skip-if = tsan || os =='android' || socketprocess_networking run-sequentially = node server exceptions dont replay well [test_websocket_with_h3_active.js] -skip-if = - tsan - os =='android' - verify && (os == 'win') - socketprocess_networking +skip-if = tsan || os =='android' || (verify && (os == 'win')) || socketprocess_networking run-sequentially = node server exceptions dont replay well [test_304_headers.js] [test_http3_direct_proxy.js] -skip-if = - tsan - os =='android' +skip-if = tsan || os =='android' run-sequentially = node server exceptions dont replay well [test_bug1725766.js] skip-if = os == "android" # skip because of bug 1589327 From 29bbbf1b43b8e8196293ffae07e080e0464a8051 Mon Sep 17 00:00:00 2001 From: Norisz Fay Date: Thu, 21 Oct 2021 16:41:26 +0300 Subject: [PATCH 30/35] Backed out 3 changesets (bug 1694085) for causing mda failures on test_HEAAC_extradata.html CLOSED TREE Backed out changeset 1468166a95c6 (bug 1694085) Backed out changeset dd1ec2d464cb (bug 1694085) Backed out changeset f80df57030d8 (bug 1694085) --- dom/media/ipc/MediaIPCUtils.h | 4 +- dom/media/mediasource/test/mochitest.ini | 2 - .../test/test_HEAAC_extradata.html | 88 ------------------ .../mediasource/test/whitenoise-he-aac-5s.mp4 | Bin 27078 -> 0 bytes 4 files changed, 1 insertion(+), 93 deletions(-) delete mode 100644 dom/media/mediasource/test/test_HEAAC_extradata.html delete mode 100644 dom/media/mediasource/test/whitenoise-he-aac-5s.mp4 diff --git a/dom/media/ipc/MediaIPCUtils.h b/dom/media/ipc/MediaIPCUtils.h index ed9f9006b88cd..b940a3af8ba47 100644 --- a/dom/media/ipc/MediaIPCUtils.h +++ b/dom/media/ipc/MediaIPCUtils.h @@ -95,7 +95,6 @@ struct ParamTraits { WriteParam(aMsg, aParam.mProfile); WriteParam(aMsg, aParam.mExtendedProfile); WriteParam(aMsg, *aParam.mCodecSpecificConfig); - WriteParam(aMsg, *aParam.mExtraData); } static bool Read(const Message* aMsg, PickleIterator* aIter, @@ -107,8 +106,7 @@ struct ParamTraits { ReadParam(aMsg, aIter, &aResult->mBitDepth) && ReadParam(aMsg, aIter, &aResult->mProfile) && ReadParam(aMsg, aIter, &aResult->mExtendedProfile) && - ReadParam(aMsg, aIter, aResult->mCodecSpecificConfig.get()) && - ReadParam(aMsg, aIter, aResult->mExtraData.get())) { + ReadParam(aMsg, aIter, aResult->mCodecSpecificConfig.get())) { return true; } return false; diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index 7549c3c7d9719..ed43ac8376a2c 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -55,7 +55,6 @@ support-files = wmf_mismatchedaudiotime.mp4 bug1718709_low_res.mp4 bug1718709_high_res.mp4 - whitenoise-he-aac-5s.mp4 [test_AbortAfterPartialMediaSegment.html] [test_AppendPartialInitSegment.html] @@ -86,7 +85,6 @@ skip-if = os == 'win' # bug 1487973, (os == 'mac') # mac due to bug 1487973 [test_HaveMetadataUnbufferedSeek.html] [test_HaveMetadataUnbufferedSeek_mp4.html] -[test_HEAAC_extradata.html] [test_LiveSeekable.html] [test_LoadedDataFired_mp4.html] [test_LoadedMetadataFired.html] diff --git a/dom/media/mediasource/test/test_HEAAC_extradata.html b/dom/media/mediasource/test/test_HEAAC_extradata.html deleted file mode 100644 index 267c4ff2e637a..0000000000000 --- a/dom/media/mediasource/test/test_HEAAC_extradata.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - HE-AAC decoding test - - - - - -
-
-
- - diff --git a/dom/media/mediasource/test/whitenoise-he-aac-5s.mp4 b/dom/media/mediasource/test/whitenoise-he-aac-5s.mp4 deleted file mode 100644 index db648b82294f4b257af22566f77c031174c7dc77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27078 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 H56%AptX&|a From 07077e4449532335030e3f0e7f7e1ed3248c63fa Mon Sep 17 00:00:00 2001 From: Frederik Braun Date: Thu, 21 Oct 2021 13:08:11 +0000 Subject: [PATCH 31/35] Bug 1736781 - Update eslint-plugin-no-unsanitized to version 3.2.0 r=Standard8 Differential Revision: https://phabricator.services.mozilla.com/D128988 --- package-lock.json | 473 +++++++++++++++++++--------------- package.json | 2 +- tools/lint/eslint/manifest.tt | 4 +- 3 files changed, 262 insertions(+), 217 deletions(-) diff --git a/package-lock.json b/package-lock.json index a3f23c7e7e36d..2fa459860c02c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,9 +4,9 @@ "lockfileVersion": 1, "dependencies": { "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { "@babel/highlight": "^7.14.5" @@ -62,20 +62,20 @@ } }, "@babel/generator": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.0.tgz", - "integrity": "sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.15.0", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-compilation-targets": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz", - "integrity": "sha512-h+/9t0ncd4jfZ8wsdAsoIxSa61qhBYlycXiHWqJaQBCXAhDCMbPRSMTGnZIkkmt1u4ag+UQmuqcILwqKzZ4N2A==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", "dev": true, "requires": { "@babel/compat-data": "^7.15.0", @@ -85,75 +85,75 @@ } }, "@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz", - "integrity": "sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", "dev": true, "requires": { - "@babel/types": "^7.15.0" + "@babel/types": "^7.15.4" } }, "@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-module-transforms": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz", - "integrity": "sha512-RkGiW5Rer7fpXv9m1B3iHIFDZdItnO2/BLfWVW/9q7+KqQSDY5kUfQEbzdXM1MVhJGcugKV7kRrNVzNxmk7NBg==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.15.0", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.9", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" } }, "@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-plugin-utils": { @@ -163,39 +163,39 @@ "dev": true }, "@babel/helper-replace-supers": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.0.tgz", - "integrity": "sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.15.0", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", "dev": true, "requires": { - "@babel/types": "^7.14.8" + "@babel/types": "^7.15.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/types": "^7.15.4" } }, "@babel/helper-validator-identifier": { - "version": "7.14.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", - "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/helper-validator-option": { @@ -205,14 +205,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.3.tgz", - "integrity": "sha512-HwJiz52XaS96lX+28Tnbu31VeFSQJGOeKHJeaEPQlTl7PnlhFElWPj8tUXtqFIzeN86XxXoBr+WFAyK2PPVz6g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", "dev": true, "requires": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.15.0", - "@babel/types": "^7.15.0" + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/highlight": { @@ -227,9 +227,9 @@ } }, "@babel/parser": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", - "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, "@babel/plugin-syntax-jsx": { @@ -242,18 +242,18 @@ } }, "@babel/runtime": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz", - "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.15.3.tgz", - "integrity": "sha512-30A3lP+sRL6ml8uhoJSs+8jwpKzbw8CqBvDc1laeptxPm5FahumJxirigcbD2qTs71Sonvj1cyZB0OKGAmxQ+A==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.15.4.tgz", + "integrity": "sha512-lWcAqKeB624/twtTc3w6w/2o9RqJPaNBhPGK6DKLSiwuVWC7WFkypWyNg+CpZoyJH0jVzv1uMtXZ/5/lQOLtCg==", "dev": true, "requires": { "core-js-pure": "^3.16.0", @@ -261,37 +261,37 @@ } }, "@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/traverse": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.0.tgz", - "integrity": "sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.0", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.15.0", - "@babel/types": "^7.15.0", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", - "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.14.9", @@ -349,6 +349,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@typescript-eslint/experimental-utils": { "version": "2.34.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", @@ -424,9 +430,9 @@ "dev": true }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -458,39 +464,38 @@ } }, "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", + "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "is-string": "^1.0.7" } }, "array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "es-abstract": "^1.19.0" } }, "array.prototype.flatmap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", - "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", + "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "function-bind": "^1.1.1" + "es-abstract": "^1.19.0" } }, "ast-types-flow": { @@ -506,9 +511,9 @@ "dev": true }, "axe-core": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.2.tgz", - "integrity": "sha512-5LMaDRWm8ZFPAEdzTYmgjjEdj1YnQcpfrVajO/sn/LhbpGp0Y0H64c2hLZI1gRMxfA+w1S71Uc/nHaOXgcCvGg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.3.tgz", + "integrity": "sha512-/lqqLAmuIPi79WYfRpy2i8z+x+vxU3zX2uAm0gs1q52qTuKwolOj1P8XbufpXcsydrpKx2yGn2wzAnxCMV86QA==", "dev": true }, "axobject-query": { @@ -540,16 +545,16 @@ } }, "browserslist": { - "version": "4.16.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", - "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.4.tgz", + "integrity": "sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001251", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.811", + "caniuse-lite": "^1.0.30001265", + "electron-to-chromium": "^1.3.867", "escalade": "^3.1.1", - "node-releases": "^1.1.75" + "node-releases": "^2.0.0", + "picocolors": "^1.0.0" } }, "call-bind": { @@ -569,9 +574,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001251", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz", - "integrity": "sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A==", + "version": "1.0.30001270", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001270.tgz", + "integrity": "sha512-TcIC7AyNWXhcOmv2KftOl1ShFAaHQYcB/EPL/hEyMrcS7ZX0/DvV1aoy6BzV0+16wTpoAyTMGDNAJfSqS/rz7A==", "dev": true }, "catharsis": { @@ -609,12 +614,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "colorette": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", - "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -631,9 +630,9 @@ } }, "core-js-pure": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.16.2.tgz", - "integrity": "sha512-oxKe64UH049mJqrKkynWp6Vu0Rlm/BTXO/bJZuN2mmR3RtOFNepLlSWDd1eo16PzHpQAoNG97rLU1V/YxesJjw==", + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.18.3.tgz", + "integrity": "sha512-qfskyO/KjtbYn09bn1IPkuhHl5PlJ6IzJ9s9sraJ1EqcuGyLGKzhSM1cY0zgyL9hx42eulQLZ6WaeK5ycJCkqw==", "dev": true }, "cross-spawn": { @@ -663,9 +662,9 @@ } }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "define-properties": { @@ -704,18 +703,18 @@ "dev": true }, "domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", "dev": true, "requires": { "domelementtype": "^2.2.0" } }, "domutils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", - "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, "requires": { "dom-serializer": "^1.0.1", @@ -724,9 +723,9 @@ } }, "electron-to-chromium": { - "version": "1.3.811", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.811.tgz", - "integrity": "sha512-hv3kgf6YSd+jQ7J+7Kdm44yux/1vxcAwfGV/6M6Nq4E9zJ3Bml/P2+vULCvqLS6Lh9knBCQ7iEMvyeDiGe5EbA==", + "version": "1.3.876", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.876.tgz", + "integrity": "sha512-a6LR4738psrubCtGx5HxM/gNlrIsh4eFTNnokgOqvQo81GWd07lLcOjITkAXn2y4lIp18vgS+DGnehj+/oEAxQ==", "dev": true }, "emoji-regex": { @@ -760,22 +759,25 @@ } }, "es-abstract": { - "version": "1.18.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", - "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", "has-symbols": "^1.0.2", "internal-slot": "^1.0.3", - "is-callable": "^1.2.3", + "is-callable": "^1.2.4", "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.3", - "is-string": "^1.0.6", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", "object-inspect": "^1.11.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", @@ -967,12 +969,13 @@ } }, "eslint-module-utils": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz", - "integrity": "sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz", + "integrity": "sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==", "dev": true, "requires": { "debug": "^3.2.7", + "find-up": "^2.1.0", "pkg-dir": "^2.0.0" }, "dependencies": { @@ -1108,9 +1111,9 @@ } }, "eslint-plugin-no-unsanitized": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-3.1.5.tgz", - "integrity": "sha512-s/6w++p1590h/H/dE2Wo660bOkaM/3OEK14Y7xm1UT0bafxkKw1Cq0ksjxkxLdH/WWd014DlsLKuD6CyNrR2Dw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-3.2.0.tgz", + "integrity": "sha512-92opuXbjWmXcod94EyCKhp36V1QHLM/ArAST2ssgKOojALne0eZvSPfrg4oyr0EwTXvy0RJNe/Tkm33VkDUrKQ==", "dev": true }, "eslint-plugin-prettier": { @@ -1370,10 +1373,20 @@ "has-symbols": "^1.0.1" } }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1540,9 +1553,9 @@ "dev": true }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "requires": { "has": "^1.0.3" @@ -1570,9 +1583,9 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -1603,6 +1616,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -1621,6 +1640,15 @@ "has-symbols": "^1.0.2" } }, + "is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1716,12 +1744,12 @@ } }, "jsx-ast-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", - "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", + "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", "dev": true, "requires": { - "array-includes": "^3.1.2", + "array-includes": "^3.1.3", "object.assign": "^4.1.2" } }, @@ -1914,9 +1942,9 @@ "dev": true }, "node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", "dev": true }, "normalize-package-data": { @@ -1970,37 +1998,36 @@ } }, "object.entries": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", - "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "es-abstract": "^1.19.1" } }, "object.fromentries": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", - "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" + "es-abstract": "^1.19.1" } }, "object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "es-abstract": "^1.19.1" } }, "once": { @@ -2102,6 +2129,12 @@ "pify": "^3.0.0" } }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -2385,25 +2418,25 @@ "dev": true }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "string.prototype.matchall": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz", - "integrity": "sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", + "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2", + "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", "has-symbols": "^1.0.2", "internal-slot": "^1.0.3", @@ -2432,12 +2465,12 @@ } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { @@ -2462,23 +2495,23 @@ } }, "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.2.tgz", + "integrity": "sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==", "dev": true, "requires": { "ajv": "^8.0.1", "lodash.clonedeep": "^4.5.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "dependencies": { "ajv": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", - "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -2514,14 +2547,26 @@ "dev": true }, "tsconfig-paths": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", - "integrity": "sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz", + "integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==", "dev": true, "requires": { - "json5": "^2.2.0", + "@types/json5": "^0.0.29", + "json5": "^1.0.1", "minimist": "^1.2.0", "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } } }, "tslib": { diff --git a/package.json b/package.json index fa37a0f1ee3b3..20474204eff23 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "eslint-plugin-jest": "23.20.0", "eslint-plugin-jsx-a11y": "6.4.1", "eslint-plugin-mozilla": "file:tools/lint/eslint/eslint-plugin-mozilla", - "eslint-plugin-no-unsanitized": "3.1.5", + "eslint-plugin-no-unsanitized": "3.2.0", "eslint-plugin-prettier": "3.4.0", "eslint-plugin-react": "7.24.0", "eslint-plugin-spidermonkey-js": "file:tools/lint/eslint/eslint-plugin-spidermonkey-js", diff --git a/tools/lint/eslint/manifest.tt b/tools/lint/eslint/manifest.tt index 4228993e8ec1f..f923152edbed0 100644 --- a/tools/lint/eslint/manifest.tt +++ b/tools/lint/eslint/manifest.tt @@ -4,7 +4,7 @@ "visibility": "public", "filename": "eslint.tar.gz", "unpack": true, - "digest": "41389a0b3998d238f366c4785b5339b36e2c81772aad6fa1ef79f1462c588b2fe0ed2ad18aecc8ae921ab97d7b5e07d13b6e95afa4343abf95d670e4333c3074", - "size": 11954852 + "digest": "3f5a672d3c99fe057c0a23ba0a974b592d51c443241d04a698c17f81d255f328dcd77eddca529c492840f4b2f97e5d3dfcb8fd932232262488c5f5e5f5acfa42", + "size": 12049638 } ] \ No newline at end of file From 06f92be7c4bc7f6ec5d59e2f603c2d68bd44982f Mon Sep 17 00:00:00 2001 From: Itiel Date: Thu, 21 Oct 2021 13:09:36 +0000 Subject: [PATCH 32/35] Bug 1736240 - Make menulists and buttons in the permissions panel have the same opacity r=johannh Differential Revision: https://phabricator.services.mozilla.com/D128693 --- browser/themes/shared/controlcenter/panel.inc.css | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/themes/shared/controlcenter/panel.inc.css b/browser/themes/shared/controlcenter/panel.inc.css index b38070d998135..727ec28e9b525 100644 --- a/browser/themes/shared/controlcenter/panel.inc.css +++ b/browser/themes/shared/controlcenter/panel.inc.css @@ -718,7 +718,6 @@ description#identity-popup-content-verifier, padding-inline-end: 6px; background-color: var(--button-bgcolor); color: var(--button-color); - opacity: 0.6; border-radius: 4px; } From d8968370de6228be4f2a9972c8708b8bbb014948 Mon Sep 17 00:00:00 2001 From: stransky Date: Thu, 21 Oct 2021 13:15:02 +0000 Subject: [PATCH 33/35] Bug 1735095 [Wayland] Polish cursorOffset logging, r=jhorak Differential Revision: https://phabricator.services.mozilla.com/D129153 --- widget/gtk/nsWindow.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 8c3b6bd423265..656039db5ec21 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -2180,6 +2180,12 @@ void nsWindow::WaylandPopupMove() { p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor(); } + // Although we have mPopupPosition / mRelativePopupPosition here + // we can't use it. move-to-rect needs anchor rectangle to position a popup + // but we have only a point from Resize(). + // + // So we need to extract popup position from nsMenuPopupFrame() and duplicate + // the layout work here. nsRect anchorRectAppUnits = popupFrame->GetAnchorRect(); anchorRect = LayoutDeviceIntRect::FromUnknownRect( anchorRectAppUnits.ToNearestPixels(p2a)); @@ -2304,6 +2310,8 @@ void nsWindow::WaylandPopupMove() { cursorOffset.MoveBy(margin.left, margin.top); break; } + cursorOffset.x /= p2a; + cursorOffset.y /= p2a; } if (!g_signal_handler_find(gdkWindow, G_SIGNAL_MATCH_FUNC, 0, 0, nullptr, @@ -2312,8 +2320,8 @@ void nsWindow::WaylandPopupMove() { G_CALLBACK(NativeMoveResizeCallback), this); } - LOG_POPUP(" popup window cursor offset x: %d y: %d\n", cursorOffset.x / p2a, - cursorOffset.y / p2a); + LOG_POPUP(" popup window cursor offset x: %d y: %d\n", cursorOffset.x, + cursorOffset.y); GdkRectangle rect = {anchorRect.x, anchorRect.y, anchorRect.width, anchorRect.height}; @@ -2331,7 +2339,7 @@ void nsWindow::WaylandPopupMove() { LOG_POPUP(" move-to-rect call"); mPopupLastAnchor = anchorRect; sGdkWindowMoveToRect(gdkWindow, &rect, rectAnchor, menuAnchor, hints, - cursorOffset.x / p2a, cursorOffset.y / p2a); + cursorOffset.x, cursorOffset.y); } void nsWindow::SetZIndex(int32_t aZIndex) { From d86a1ff3dfb77f012855da38c6e1ce2dbb2710f8 Mon Sep 17 00:00:00 2001 From: stransky Date: Thu, 21 Oct 2021 13:15:02 +0000 Subject: [PATCH 34/35] Bug 1735095 [Wayland] When font/text scale factor is used, don't reposition popups for +-1 pixel moves, r=jhorak When font scale is used, move-to-rect popup positiong may end up in endless loop due to rounding error while popup position oscilates around layout position. We don't have a fox for that so let's disable such small popup movements when font scale is used. Depends on D129153 Differential Revision: https://phabricator.services.mozilla.com/D129154 --- widget/gtk/nsWindow.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 656039db5ec21..153cdc351cbba 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -1893,6 +1893,24 @@ void nsWindow::NativeMoveResizeWaylandPopupCallback( } if (needsPositionUpdate) { + // See Bug 1735095 + // Font scale causes rounding errors which we can't handle by move-to-rect. + // The font scale should not be used, but let's handle it somehow to + // avoid endless move calls. + if (StaticPrefs::layout_css_devPixelsPerPx() > 0 || + gfxPlatformGtk::GetFontScaleFactor() != 1) { + bool roundingError = (abs(newBounds.x - mBounds.x) < 2 && + abs(newBounds.y - mBounds.y) < 2); + if (roundingError) { + // Keep the window where it is. + GdkPoint topLeft = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft()); + LOG_POPUP(" apply rounding error workaround, move to %d, %d", + topLeft.x, topLeft.y); + gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y); + return; + } + } + LOG_POPUP(" needPositionUpdate, new bounds [%d, %d]", newBounds.x, newBounds.y); mBounds.x = newBounds.x; From 921cb99bcc3960414b079e11282d5b13bc7fd30c Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 21 Oct 2021 19:25:20 +0000 Subject: [PATCH 35/35] Bug 1736373 - Revert to pingsender1. r=chutten Differential Revision: https://phabricator.services.mozilla.com/D129196 --- toolkit/components/telemetry/app/TelemetrySend.jsm | 6 ++---- .../telemetry/tests/unit/test_HealthPing.js | 12 +++++++++--- .../telemetry/tests/unit/test_PingSender.js | 4 ++-- .../telemetry/tests/unit/test_TelemetryController.js | 12 +++++++++--- .../telemetry/tests/unit/test_TelemetrySession.js | 8 +++++++- toolkit/components/telemetry/tests/unit/xpcshell.ini | 2 +- 6 files changed, 30 insertions(+), 14 deletions(-) diff --git a/toolkit/components/telemetry/app/TelemetrySend.jsm b/toolkit/components/telemetry/app/TelemetrySend.jsm index 14fc40e9944e3..56c830aef2100 100644 --- a/toolkit/components/telemetry/app/TelemetrySend.jsm +++ b/toolkit/components/telemetry/app/TelemetrySend.jsm @@ -1638,14 +1638,12 @@ var TelemetrySendImpl = { } const exeName = - AppConstants.MOZ_APP_NAME + - (AppConstants.platform === "win" ? ".exe" : ""); + AppConstants.platform === "win" ? "pingsender.exe" : "pingsender"; let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile); exe.append(exeName); - let params = ["--backgroundtask", "pingsender"]; - params.push(...pings.flatMap(ping => [ping.url, ping.path])); + let params = pings.flatMap(ping => [ping.url, ping.path]); let process = Cc["@mozilla.org/process/util;1"].createInstance( Ci.nsIProcess ); diff --git a/toolkit/components/telemetry/tests/unit/test_HealthPing.js b/toolkit/components/telemetry/tests/unit/test_HealthPing.js index a96e83ca3c5e6..62d887e174980 100644 --- a/toolkit/components/telemetry/tests/unit/test_HealthPing.js +++ b/toolkit/components/telemetry/tests/unit/test_HealthPing.js @@ -337,8 +337,14 @@ add_task(async function test_discardedForSizePending() { }); add_task(async function test_usePingSenderOnShutdown() { - if (gIsAndroid) { + if ( + gIsAndroid || + (AppConstants.platform == "linux" && OS.Constants.Sys.bits == 32) + ) { // We don't support the pingsender on Android, yet, see bug 1335917. + // We also don't support the pingsender testing on Treeherder for + // Linux 32 bit (due to missing libraries). So skip it there too. + // See bug 1310703 comment 78. return; } @@ -370,12 +376,12 @@ add_task(async function test_usePingSenderOnShutdown() { // Check that the health ping is sent at shutdown using the pingsender. Assert.equal( request.getHeader("User-Agent"), - "pingsender/2.0", + "pingsender/1.0", "Should have received the correct user agent string." ); Assert.equal( request.getHeader("X-PingSender-Version"), - "2.0", + "1.0", "Should have received the correct PingSender version string." ); }); diff --git a/toolkit/components/telemetry/tests/unit/test_PingSender.js b/toolkit/components/telemetry/tests/unit/test_PingSender.js index f54be574450a5..f070f7912d7fe 100644 --- a/toolkit/components/telemetry/tests/unit/test_PingSender.js +++ b/toolkit/components/telemetry/tests/unit/test_PingSender.js @@ -135,12 +135,12 @@ add_task(async function test_pingSender() { Assert.equal( req.getHeader("User-Agent"), - "pingsender/2.0", + "pingsender/1.0", "Should have received the correct user agent string." ); Assert.equal( req.getHeader("X-PingSender-Version"), - "2.0", + "1.0", "Should have received the correct PingSender version string." ); Assert.equal( diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js index b3da02b99a059..fe0a9b19a4427 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js @@ -730,8 +730,14 @@ add_task(async function test_telemetryCleanFHRDatabase() { }); add_task(async function test_sendNewProfile() { - if (gIsAndroid) { + if ( + gIsAndroid || + (AppConstants.platform == "linux" && OS.Constants.Sys.bits == 32) + ) { // We don't support the pingsender on Android, yet, see bug 1335917. + // We also don't suppor the pingsender testing on Treeherder for + // Linux 32 bit (due to missing libraries). So skip it there too. + // See bug 1310703 comment 78. return; } @@ -804,12 +810,12 @@ add_task(async function test_sendNewProfile() { // Check that the new-profile ping is sent at shutdown using the pingsender. Assert.equal( req.getHeader("User-Agent"), - "pingsender/2.0", + "pingsender/1.0", "Should have received the correct user agent string." ); Assert.equal( req.getHeader("X-PingSender-Version"), - "2.0", + "1.0", "Should have received the correct PingSender version string." ); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js index 88d6741d9191d..3c7e845b2dd1f 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js @@ -1235,8 +1235,14 @@ add_task(async function test_savedPingsOnShutdown() { }); add_task(async function test_sendShutdownPing() { - if (gIsAndroid) { + if ( + gIsAndroid || + (AppConstants.platform == "linux" && OS.Constants.Sys.bits == 32) + ) { // We don't support the pingsender on Android, yet, see bug 1335917. + // We also don't suppor the pingsender testing on Treeherder for + // Linux 32 bit (due to missing libraries). So skip it there too. + // See bug 1310703 comment 78. return; } diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.ini b/toolkit/components/telemetry/tests/unit/xpcshell.ini index 5c56f3a3e1749..43f1344d3eb73 100644 --- a/toolkit/components/telemetry/tests/unit/xpcshell.ini +++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini @@ -94,7 +94,7 @@ skip-if = apple_silicon # bug 1707747 apple_catalina # Bug 1713329 [test_PingSender.js] -skip-if = os == "android" # pingsender is disabled on Android in TelemetrySend.jsm +skip-if = (os == "android") || (os == "linux" && bits == 32) [test_TelemetryAndroidEnvironment.js] [test_TelemetryUtils.js] [test_ThirdPartyModulesPing.js]