storage: Isolate sessionStorage per top-level browsing context and copy sessionStorage when creating a new auxiliary browsing context (#37803)

This pull request introduces changes to the storage subsystem to:
- Isolate sessionStorage per top-level browsing context (WebViewId), in
  addition to origin.
- Copy sessionStorage when creating a new auxiliary browsing context
without
  noopener, as required by the corresponding spec
 
These changes bring Servo closer to spec compliance, matching expected
browser
behavior.

Testing: This work affects observable behavior. As a result, some
previously
failing WPT tests now pass. No new tests are added, since the behavior
is
already covered by existing web-platform-tests.

Fixes: #21291

---------

Signed-off-by: Jan Varga <jan.varga@gmail.com>
This commit is contained in:
Jan Varga 2025-07-04 11:15:12 +02:00 committed by GitHub
parent aaf04883dd
commit 934b3341d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 234 additions and 122 deletions

View file

@ -2,6 +2,7 @@
* 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 base::id::WebViewId;
use constellation_traits::ScriptToConstellationMessage;
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSender;
@ -49,6 +50,10 @@ impl Storage {
)
}
fn webview_id(&self) -> WebViewId {
self.global().as_window().window_proxy().webview_id()
}
fn get_url(&self) -> ServoUrl {
self.global().get_url()
}
@ -66,8 +71,9 @@ impl StorageMethods<crate::DomTypeHolder> for Storage {
self.get_storage_thread()
.send(StorageThreadMsg::Length(
sender,
self.get_url(),
self.storage_type,
self.webview_id(),
self.get_url(),
))
.unwrap();
receiver.recv().unwrap() as u32
@ -80,8 +86,9 @@ impl StorageMethods<crate::DomTypeHolder> for Storage {
self.get_storage_thread()
.send(StorageThreadMsg::Key(
sender,
self.get_url(),
self.storage_type,
self.webview_id(),
self.get_url(),
index,
))
.unwrap();
@ -93,7 +100,13 @@ impl StorageMethods<crate::DomTypeHolder> for Storage {
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
let name = String::from(name);
let msg = StorageThreadMsg::GetItem(sender, self.get_url(), self.storage_type, name);
let msg = StorageThreadMsg::GetItem(
sender,
self.storage_type,
self.webview_id(),
self.get_url(),
name,
);
self.get_storage_thread().send(msg).unwrap();
receiver.recv().unwrap().map(DOMString::from)
}
@ -106,8 +119,9 @@ impl StorageMethods<crate::DomTypeHolder> for Storage {
let msg = StorageThreadMsg::SetItem(
sender,
self.get_url(),
self.storage_type,
self.webview_id(),
self.get_url(),
name.clone(),
value.clone(),
);
@ -128,8 +142,13 @@ impl StorageMethods<crate::DomTypeHolder> for Storage {
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
let name = String::from(name);
let msg =
StorageThreadMsg::RemoveItem(sender, self.get_url(), self.storage_type, name.clone());
let msg = StorageThreadMsg::RemoveItem(
sender,
self.storage_type,
self.webview_id(),
self.get_url(),
name.clone(),
);
self.get_storage_thread().send(msg).unwrap();
if let Some(old_value) = receiver.recv().unwrap() {
self.broadcast_change_notification(Some(name), Some(old_value), None);
@ -143,8 +162,9 @@ impl StorageMethods<crate::DomTypeHolder> for Storage {
self.get_storage_thread()
.send(StorageThreadMsg::Clear(
sender,
self.get_url(),
self.storage_type,
self.webview_id(),
self.get_url(),
))
.unwrap();
if receiver.recv().unwrap() {
@ -159,8 +179,9 @@ impl StorageMethods<crate::DomTypeHolder> for Storage {
self.get_storage_thread()
.send(StorageThreadMsg::Keys(
sender,
self.get_url(),
self.storage_type,
self.webview_id(),
self.get_url(),
))
.unwrap();
receiver

View file

@ -32,7 +32,9 @@ use js::jsval::{NullValue, PrivateValue, UndefinedValue};
use js::rust::wrappers::{JS_TransplantObject, NewWindowProxy, SetWindowProxy};
use js::rust::{Handle, MutableHandle, MutableHandleValue, get_object_class};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use net_traits::IpcSend;
use net_traits::request::Referrer;
use net_traits::storage_thread::StorageThreadMsg;
use script_traits::NewLayoutInfo;
use serde::{Deserialize, Serialize};
use servo_url::{ImmutableOrigin, ServoUrl};
@ -334,8 +336,6 @@ impl WindowProxy {
theme: window.theme(),
};
ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
// TODO: if noopener is false, copy the sessionStorage storage area of the creator origin.
// See step 14 of https://html.spec.whatwg.org/multipage/#creating-a-new-browsing-context
let new_window_proxy = ScriptThread::find_document(response.new_pipeline_id)
.and_then(|doc| doc.browsing_context())?;
if name.to_lowercase() != "_blank" {
@ -343,6 +343,26 @@ impl WindowProxy {
}
if noopener {
new_window_proxy.disown();
} else {
// After creating a new auxiliary browsing context and document,
// the session storage is copied over.
// See https://html.spec.whatwg.org/multipage/#the-sessionstorage-attribute
let (sender, receiver) = ipc::channel().unwrap();
let msg = StorageThreadMsg::Clone {
sender,
src: window.window_proxy().webview_id(),
dest: response.new_webview_id,
};
document
.global()
.resource_threads()
.sender()
.send(msg)
.unwrap();
receiver.recv().unwrap();
}
Some(new_window_proxy)
}