1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! Holds a webview's requested zoom until its main frame commits.
//!
//! Chromium ignores a zoom set before the main frame has a live renderer and a
//! committed URL, so the level is held and applied once at the first commit.
use std::cell::Cell;
use std::rc::Rc;
/// A zoom state shared between a webview's browser entry and its load handler.
pub type SharedInitialZoom = Rc<Cell<InitialZoom>>;
/// Where a webview is on the way to its first main-frame commit.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InitialZoom {
/// The main frame has not committed yet; holds the level to apply at commit, if any.
Waiting(Option<f64>),
/// The main frame has committed; zoom changes are sent immediately.
Committed,
}
/// What to do with a zoom request.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ZoomRequest {
/// The level was stored for the first commit.
Held,
/// The level must be sent to CEF now.
SendNow,
}
impl InitialZoom {
/// Starts waiting with `level`; the default level `0.0` holds nothing.
pub fn waiting(level: f64) -> Self {
Self::Waiting(Self::held(level))
}
/// Stores `level` while waiting, or asks for it to be sent once committed.
pub fn request(&mut self, level: f64) -> ZoomRequest {
match self {
Self::Waiting(held) => {
*held = Self::held(level);
ZoomRequest::Held
}
Self::Committed => ZoomRequest::SendNow,
}
}
/// Returns the held level once and moves to [`InitialZoom::Committed`].
pub fn commit(&mut self) -> Option<f64> {
match std::mem::replace(self, Self::Committed) {
Self::Waiting(held) => held,
Self::Committed => None,
}
}
fn held(level: f64) -> Option<f64> {
(level != 0.0).then_some(level)
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Asserts that a webview left at the default zoom applies nothing at commit.
///
/// Case: a webview is spawned without a zoom change and loads its page.
#[test]
fn a_default_zoom_applies_nothing_at_commit() {
assert_eq!(InitialZoom::waiting(0.0).commit(), None);
}
/// Asserts that a non-default zoom is applied at the first commit only.
///
/// Case: a webview is spawned at 1.5 and later navigates, committing again.
#[test]
fn a_non_default_zoom_applies_once() {
let mut zoom = InitialZoom::waiting(1.5);
assert_eq!(zoom.commit(), Some(1.5));
assert_eq!(zoom.commit(), None);
}
/// Asserts that a change before the commit is held and replaces the earlier level.
///
/// Case: the app changes the zoom while the page is still loading.
#[test]
fn a_change_before_commit_replaces_the_held_level() {
let mut zoom = InitialZoom::waiting(1.0);
assert_eq!(zoom.request(2.0), ZoomRequest::Held);
assert_eq!(zoom.commit(), Some(2.0));
}
/// Asserts that resetting to the default before the commit applies nothing.
///
/// Case: the app zooms in and then resets the zoom while the page is still loading.
#[test]
fn resetting_to_default_before_commit_applies_nothing() {
let mut zoom = InitialZoom::waiting(1.0);
assert_eq!(zoom.request(0.0), ZoomRequest::Held);
assert_eq!(zoom.commit(), None);
}
/// Asserts that a request after the commit is sent immediately.
///
/// Case: the user zooms with the mouse wheel after the page has loaded.
#[test]
fn after_commit_changes_are_sent_now() {
let mut zoom = InitialZoom::waiting(0.0);
zoom.commit();
assert_eq!(zoom.request(3.0), ZoomRequest::SendNow);
}
}