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
/* 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/. */

use std::rc::Rc;

use bluetooth_traits::{BluetoothRequest, BluetoothResponse};
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSender;

use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::BluetoothPermissionResultBinding::BluetoothPermissionResultMethods;
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionStatus_Binding::PermissionStatusMethods;
use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
    PermissionName, PermissionState,
};
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::bluetooth::{AllowedBluetoothDevice, AsyncBluetoothListener, Bluetooth};
use crate::dom::bluetoothdevice::BluetoothDevice;
use crate::dom::globalscope::GlobalScope;
use crate::dom::permissionstatus::PermissionStatus;
use crate::dom::promise::Promise;

// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothpermissionresult
#[dom_struct]
pub struct BluetoothPermissionResult {
    status: PermissionStatus,
    devices: DomRefCell<Vec<Dom<BluetoothDevice>>>,
}

impl BluetoothPermissionResult {
    #[allow(crown::unrooted_must_root)]
    fn new_inherited(status: &PermissionStatus) -> BluetoothPermissionResult {
        let result = BluetoothPermissionResult {
            status: PermissionStatus::new_inherited(status.get_query()),
            devices: DomRefCell::new(Vec::new()),
        };
        result.status.set_state(status.State());
        result
    }

    pub fn new(
        global: &GlobalScope,
        status: &PermissionStatus,
    ) -> DomRoot<BluetoothPermissionResult> {
        reflect_dom_object(
            Box::new(BluetoothPermissionResult::new_inherited(status)),
            global,
        )
    }

    pub fn get_bluetooth(&self) -> DomRoot<Bluetooth> {
        self.global().as_window().Navigator().Bluetooth()
    }

    pub fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
        self.global().as_window().bluetooth_thread()
    }

    pub fn get_query(&self) -> PermissionName {
        self.status.get_query()
    }

    pub fn set_state(&self, state: PermissionState) {
        self.status.set_state(state)
    }

    pub fn get_state(&self) -> PermissionState {
        self.status.State()
    }

    #[allow(crown::unrooted_must_root)]
    pub fn set_devices(&self, devices: Vec<Dom<BluetoothDevice>>) {
        *self.devices.borrow_mut() = devices;
    }
}

impl BluetoothPermissionResultMethods for BluetoothPermissionResult {
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothpermissionresult-devices
    fn Devices(&self) -> Vec<DomRoot<BluetoothDevice>> {
        let device_vec: Vec<DomRoot<BluetoothDevice>> = self
            .devices
            .borrow()
            .iter()
            .map(|d| DomRoot::from_ref(&**d))
            .collect();
        device_vec
    }
}

impl AsyncBluetoothListener for BluetoothPermissionResult {
    fn handle_response(&self, response: BluetoothResponse, promise: &Rc<Promise>) {
        match response {
            // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
            // Step 3, 11, 13 - 14.
            BluetoothResponse::RequestDevice(device) => {
                self.set_state(PermissionState::Granted);
                let bluetooth = self.get_bluetooth();
                let mut device_instance_map = bluetooth.get_device_map().borrow_mut();
                if let Some(ref existing_device) = device_instance_map.get(&device.id) {
                    // https://webbluetoothcg.github.io/web-bluetooth/#request-the-bluetooth-permission
                    // Step 3.
                    self.set_devices(vec![Dom::from_ref(*existing_device)]);

                    // https://w3c.github.io/permissions/#dom-permissions-request
                    // Step 8.
                    return promise.resolve_native(self);
                }
                let bt_device = BluetoothDevice::new(
                    &self.global(),
                    DOMString::from(device.id.clone()),
                    device.name.map(DOMString::from),
                    &bluetooth,
                );
                device_instance_map.insert(device.id.clone(), Dom::from_ref(&bt_device));
                self.global()
                    .as_window()
                    .bluetooth_extra_permission_data()
                    .add_new_allowed_device(AllowedBluetoothDevice {
                        deviceId: DOMString::from(device.id),
                        mayUseGATT: true,
                    });
                // https://webbluetoothcg.github.io/web-bluetooth/#request-the-bluetooth-permission
                // Step 3.
                self.set_devices(vec![Dom::from_ref(&bt_device)]);

                // https://w3c.github.io/permissions/#dom-permissions-request
                // Step 8.
                promise.resolve_native(self);
            },
            _ => promise.reject_error(Error::Type("Something went wrong...".to_owned())),
        }
    }
}