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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* 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 https://mozilla.org/MPL/2.0/. */

//! A shareable mutable container for the DOM.

use std::cell::{BorrowError, BorrowMutError};
#[cfg(not(feature = "refcell_backtrace"))]
pub use std::cell::{Ref, RefCell, RefMut};

#[cfg(feature = "refcell_backtrace")]
pub use accountable_refcell::{ref_filter_map, Ref, RefCell, RefMut};
#[cfg(not(feature = "refcell_backtrace"))]
pub use ref_filter_map::ref_filter_map;

use crate::dom::bindings::root::{assert_in_layout, assert_in_script};

/// A mutable field in the DOM.
///
/// This extends the API of `std::cell::RefCell` to allow unsafe access in
/// certain situations, with dynamic checking in debug builds.
#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
pub struct DomRefCell<T> {
    value: RefCell<T>,
}

// Functionality specific to Servo's `DomRefCell` type
// ===================================================

impl<T> DomRefCell<T> {
    /// Return a reference to the contents.  For use in layout only.
    ///
    /// # Safety
    ///
    /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving
    /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by
    /// this method is alive is undefined behaviour.
    ///
    /// # Panics
    ///
    /// Panics if this is called from anywhere other than the layout thread
    ///
    /// Panics if the value is currently mutably borrowed.
    #[allow(unsafe_code)]
    pub unsafe fn borrow_for_layout(&self) -> &T {
        assert_in_layout();
        self.value
            .try_borrow_unguarded()
            .expect("cell is mutably borrowed")
    }

    /// Borrow the contents for the purpose of script deallocation.
    ///
    /// # Safety
    ///
    /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving
    /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by
    /// this method is alive is undefined behaviour.
    ///
    /// # Panics
    ///
    /// Panics if this is called from anywhere other than the script thread.
    #[allow(unsafe_code, clippy::mut_from_ref)]
    pub unsafe fn borrow_for_script_deallocation(&self) -> &mut T {
        assert_in_script();
        &mut *self.value.as_ptr()
    }

    /// Mutably borrow a cell for layout. Ideally this would use
    /// `RefCell::try_borrow_mut_unguarded` but that doesn't exist yet.
    ///
    /// # Safety
    ///
    /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving
    /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by
    /// this method is alive is undefined behaviour.
    ///
    /// # Panics
    ///
    /// Panics if this is called from anywhere other than the layout thread.
    #[allow(unsafe_code, clippy::mut_from_ref)]
    pub unsafe fn borrow_mut_for_layout(&self) -> &mut T {
        assert_in_layout();
        &mut *self.value.as_ptr()
    }
}

// Functionality duplicated with `std::cell::RefCell`
// ===================================================
impl<T> DomRefCell<T> {
    /// Create a new `DomRefCell` containing `value`.
    pub fn new(value: T) -> DomRefCell<T> {
        DomRefCell {
            value: RefCell::new(value),
        }
    }

    /// Immutably borrows the wrapped value.
    ///
    /// The borrow lasts until the returned `Ref` exits scope. Multiple
    /// immutable borrows can be taken out at the same time.
    ///
    /// # Panics
    ///
    /// Panics if the value is currently mutably borrowed.
    #[track_caller]
    pub fn borrow(&self) -> Ref<T> {
        self.value.borrow()
    }

    /// Mutably borrows the wrapped value.
    ///
    /// The borrow lasts until the returned `RefMut` exits scope. The value
    /// cannot be borrowed while this borrow is active.
    ///
    /// # Panics
    ///
    /// Panics if the value is currently borrowed.
    #[track_caller]
    pub fn borrow_mut(&self) -> RefMut<T> {
        self.value.borrow_mut()
    }

    /// Attempts to immutably borrow the wrapped value.
    ///
    /// The borrow lasts until the returned `Ref` exits scope. Multiple
    /// immutable borrows can be taken out at the same time.
    ///
    /// Returns `None` if the value is currently mutably borrowed.
    ///
    /// # Panics
    ///
    /// Panics if this is called off the script thread.
    pub fn try_borrow(&self) -> Result<Ref<T>, BorrowError> {
        assert_in_script();
        self.value.try_borrow()
    }

    /// Mutably borrows the wrapped value.
    ///
    /// The borrow lasts until the returned `RefMut` exits scope. The value
    /// cannot be borrowed while this borrow is active.
    ///
    /// Returns `None` if the value is currently borrowed.
    ///
    /// # Panics
    ///
    /// Panics if this is called off the script thread.
    pub fn try_borrow_mut(&self) -> Result<RefMut<T>, BorrowMutError> {
        assert_in_script();
        self.value.try_borrow_mut()
    }
}