servo: Merge #11875 - Integration and improvements of File API backends (from izgzhen:file-manager-backend); r=Manishearth
authorZhen Zhang <izgzhen@gmail.com>
Mon, 04 Jul 2016 09:15:23 -0700
changeset 339199 d5d8308847f78836c13cd2651e671e6b4ff99ffe
parent 339198 0efca26cdbe9c0e4749ce2be2b21de398cacd8fb
child 339200 70db9045abfa70e0440944258f92294eb298000c
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersManishearth
servo: Merge #11875 - Integration and improvements of File API backends (from izgzhen:file-manager-backend); r=Manishearth Basically three major changes: 1. More complete origin check in `FileManagerThreadMsg` 2. Add reference counting logic to file manage store and script API 3. Integrate the support of slicing r? @Manishearth --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 36974f0746261b971c93ed7dfb9bd726675ccf69
servo/components/net/blob_loader.rs
servo/components/net/filemanager_thread.rs
servo/components/net_traits/Cargo.toml
servo/components/net_traits/blob_url_store.rs
servo/components/net_traits/filemanager_thread.rs
servo/components/net_traits/lib.rs
servo/components/script/dom/bindings/trace.rs
servo/components/script/dom/blob.rs
servo/components/script/dom/file.rs
servo/components/script/dom/htmlinputelement.rs
servo/components/script/dom/url.rs
servo/components/servo/Cargo.lock
servo/ports/cef/Cargo.lock
servo/tests/unit/net/filemanager_thread.rs
--- a/servo/components/net/blob_loader.rs
+++ b/servo/components/net/blob_loader.rs
@@ -1,87 +1,61 @@
 /* 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 filemanager_thread::BlobURLStore;
 use hyper::header::{DispositionType, ContentDisposition, DispositionParam};
 use hyper::header::{Headers, ContentType, ContentLength, Charset};
 use hyper::http::RawStatus;
 use mime::{Mime, Attr};
 use mime_classifier::MimeClassifier;
 use net_traits::ProgressMsg::Done;
-use net_traits::blob_url_store::{parse_blob_url, BlobURLStoreEntry, BlobURLStoreError};
+use net_traits::blob_url_store::BlobURLStoreEntry;
+use net_traits::filemanager_thread::RelativePos;
 use net_traits::response::HttpsState;
-use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError};
-use resource_thread::{send_error, start_sending_sniffed_opt};
-use std::str;
-use std::sync::{Arc, RwLock};
-
+use net_traits::{LoadConsumer, LoadData, Metadata};
+use resource_thread::start_sending_sniffed_opt;
+use std::ops::Index;
+use std::sync::Arc;
 
 // TODO: Check on GET
 // https://w3c.github.io/FileAPI/#requestResponseModel
 
-pub fn load(load_data: LoadData, consumer: LoadConsumer,
-            blob_url_store: Arc<RwLock<BlobURLStore>>,
-            classifier: Arc<MimeClassifier>) { // XXX: Move it into net process later
-
-    match parse_blob_url(&load_data.url) {
-        None => {
-            let format_err = NetworkError::Internal(format!("Invalid blob URL format {:?}", load_data.url));
-            send_error(load_data.url.clone(), format_err, consumer);
-        }
-        Some((uuid, _fragment)) => {
-            match blob_url_store.read().unwrap().request(uuid, &load_data.url.origin()) {
-                Ok(entry) => load_blob(&load_data, consumer, classifier, entry),
-                Err(e) => {
-                    let err = match e {
-                        BlobURLStoreError::InvalidKey =>
-                            format!("Invalid blob URL key {:?}", uuid.simple().to_string()),
-                        BlobURLStoreError::InvalidOrigin =>
-                            format!("Invalid blob URL origin {:?}", load_data.url.origin()),
-                    };
-                    send_error(load_data.url.clone(), NetworkError::Internal(err), consumer);
-                }
-            }
-        }
-    }
-}
-
-fn load_blob(load_data: &LoadData,
-             start_chan: LoadConsumer,
-             classifier: Arc<MimeClassifier>,
-             entry: &BlobURLStoreEntry) {
+pub fn load_blob(load_data: &LoadData, start_chan: LoadConsumer,
+                 classifier: Arc<MimeClassifier>, opt_filename: Option<String>,
+                 rel_pos: &RelativePos, entry: &BlobURLStoreEntry) {
     let content_type: Mime = entry.type_string.parse().unwrap_or(mime!(Text / Plain));
     let charset = content_type.get_param(Attr::Charset);
 
     let mut headers = Headers::new();
 
-    if let Some(ref name) = entry.filename {
+    if let Some(name) = opt_filename {
         let charset = charset.and_then(|c| c.as_str().parse().ok());
         headers.set(ContentDisposition {
             disposition: DispositionType::Inline,
             parameters: vec![
                 DispositionParam::Filename(charset.unwrap_or(Charset::Us_Ascii),
                                            None, name.as_bytes().to_vec())
             ]
         });
     }
 
+    let range = rel_pos.to_abs_range(entry.size as usize);
+
     headers.set(ContentType(content_type.clone()));
-    headers.set(ContentLength(entry.size));
+    headers.set(ContentLength(range.len() as u64));
 
     let metadata = Metadata {
         final_url: load_data.url.clone(),
         content_type: Some(ContentType(content_type.clone())),
         charset: charset.map(|c| c.as_str().to_string()),
         headers: Some(headers),
         // https://w3c.github.io/FileAPI/#TwoHundredOK
         status: Some(RawStatus(200, "OK".into())),
         https_state: HttpsState::None,
     };
 
     if let Ok(chan) =
         start_sending_sniffed_opt(start_chan, metadata, classifier,
-                                  &entry.bytes, load_data.context.clone()) {
+                                  &entry.bytes.index(range), load_data.context.clone()) {
         let _ = chan.send(Done(Ok(())));
     }
 }
--- a/servo/components/net/filemanager_thread.rs
+++ b/servo/components/net/filemanager_thread.rs
@@ -1,27 +1,30 @@
 /* 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 blob_loader;
+use blob_loader::load_blob;
 use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
 use mime_classifier::MimeClassifier;
 use mime_guess::guess_mime_type_opt;
-use net_traits::blob_url_store::{BlobURLStoreEntry, BlobURLStoreError, BlobURLStoreMsg};
-use net_traits::filemanager_thread::{FileManagerThreadMsg, FileManagerResult, FilterPattern};
-use net_traits::filemanager_thread::{SelectedFile, FileManagerThreadError, SelectedFileId};
+use net_traits::blob_url_store::{BlobURLStoreEntry, BlobURLStoreError, parse_blob_url};
+use net_traits::filemanager_thread::{FileManagerThreadMsg, FileManagerResult, FilterPattern, FileOrigin};
+use net_traits::filemanager_thread::{SelectedFile, RelativePos, FileManagerThreadError, SelectedFileId};
+use net_traits::{LoadConsumer, LoadData, NetworkError};
+use resource_thread::send_error;
+use std::cell::Cell;
 use std::collections::HashMap;
 use std::fs::File;
 use std::io::Read;
 use std::path::{Path, PathBuf};
-use std::sync::{Arc, RwLock};
+use std::sync::Arc;
 #[cfg(any(target_os = "macos", target_os = "linux"))]
 use tinyfiledialogs;
-use url::{Url, Origin};
+use url::Url;
 use util::thread::spawn_named;
 use uuid::Uuid;
 
 pub trait FileManagerThreadFactory<UI: 'static + UIProvider> {
     fn new(&'static UI) -> Self;
 }
 
 pub trait UIProvider where Self: Sync {
@@ -82,110 +85,195 @@ impl<UI: 'static + UIProvider> FileManag
         spawn_named("FileManager".to_owned(), move || {
             FileManager::new(recv, ui).start();
         });
 
         chan
     }
 }
 
+struct FileStoreEntry {
+    /// Origin of the entry's "creator"
+    origin: FileOrigin,
+    /// Backend implementation
+    file_impl: FileImpl,
+    /// Reference counting
+    refs: Cell<usize>,
+}
+
+/// File backend implementation
+enum FileImpl {
+    PathOnly(PathBuf),
+    Memory(BlobURLStoreEntry),
+    Sliced(Uuid, RelativePos),
+}
+
 struct FileManager<UI: 'static + UIProvider> {
     receiver: IpcReceiver<FileManagerThreadMsg>,
-    idmap: HashMap<Uuid, PathBuf>,
+    store: HashMap<Uuid, FileStoreEntry>,
     classifier: Arc<MimeClassifier>,
-    blob_url_store: Arc<RwLock<BlobURLStore>>,
     ui: &'static UI,
 }
 
 impl<UI: 'static + UIProvider> FileManager<UI> {
     fn new(recv: IpcReceiver<FileManagerThreadMsg>, ui: &'static UI) -> FileManager<UI> {
         FileManager {
             receiver: recv,
-            idmap: HashMap::new(),
+            store: HashMap::new(),
             classifier: Arc::new(MimeClassifier::new()),
-            blob_url_store: Arc::new(RwLock::new(BlobURLStore::new())),
-            ui: ui
+            ui: ui,
         }
     }
 
     /// Start the file manager event loop
     fn start(&mut self) {
         loop {
             match self.receiver.recv().unwrap() {
-                FileManagerThreadMsg::SelectFile(filter, sender) => self.select_file(filter, sender),
-                FileManagerThreadMsg::SelectFiles(filter, sender) => self.select_files(filter, sender),
-                FileManagerThreadMsg::ReadFile(sender, id) => {
-                    match self.try_read_file(id) {
+                FileManagerThreadMsg::SelectFile(filter, sender, origin) => self.select_file(filter, sender, origin),
+                FileManagerThreadMsg::SelectFiles(filter, sender, origin) => self.select_files(filter, sender, origin),
+                FileManagerThreadMsg::ReadFile(sender, id, origin) => {
+                    match self.try_read_file(id, origin) {
                         Ok(buffer) => { let _ = sender.send(Ok(buffer)); }
                         Err(_) => { let _ = sender.send(Err(FileManagerThreadError::ReadFileError)); }
                     }
                 }
-                FileManagerThreadMsg::DeleteFileID(id) => self.delete_fileid(id),
-                FileManagerThreadMsg::BlobURLStoreMsg(msg) => self.blob_url_store.write().unwrap().process(msg),
+                FileManagerThreadMsg::TransferMemory(entry, rel_pos, sender, origin) =>
+                    self.transfer_memory(entry, rel_pos, sender, origin),
+                FileManagerThreadMsg::AddSlicedEntry(id, rel_pos, sender, origin) =>
+                    self.add_sliced_entry(id, rel_pos, sender, origin),
                 FileManagerThreadMsg::LoadBlob(load_data, consumer) => {
-                    blob_loader::load(load_data, consumer,
-                                      self.blob_url_store.clone(),
-                                      self.classifier.clone());
+                    match parse_blob_url(&load_data.url) {
+                        None => {
+                            let e = format!("Invalid blob URL format {:?}", load_data.url);
+                            let format_err = NetworkError::Internal(e);
+                            send_error(load_data.url.clone(), format_err, consumer);
+                        }
+                        Some((id, _fragment)) => {
+                            self.process_request(&load_data, consumer, &RelativePos::full_range(), &id);
+                        }
+                    }
                 },
+                FileManagerThreadMsg::DecRef(id, origin) => {
+                    if let Ok(id) = Uuid::parse_str(&id.0) {
+                        self.dec_ref(id, origin);
+                    }
+                }
+                FileManagerThreadMsg::IncRef(id, origin) => {
+                    if let Ok(id) = Uuid::parse_str(&id.0) {
+                        self.inc_ref(id, origin);
+                    }
+                }
                 FileManagerThreadMsg::Exit => break,
             };
         }
     }
 
+    fn inc_ref(&mut self, id: Uuid, origin_in: FileOrigin) {
+        match self.store.get(&id) {
+            Some(entry) => {
+                if entry.origin == origin_in {
+                    entry.refs.set(entry.refs.get() + 1);
+                }
+            }
+            None => return, // Invalid UUID
+        }
+    }
+
+    fn add_sliced_entry(&mut self, id: SelectedFileId, rel_pos: RelativePos,
+                        sender: IpcSender<Result<SelectedFileId, BlobURLStoreError>>,
+                        origin_in: FileOrigin) {
+        if let Ok(id) = Uuid::parse_str(&id.0) {
+            match self.store.get(&id) {
+                Some(entry) => {
+                    if entry.origin == origin_in {
+                        // inc_ref on parent entry
+                        entry.refs.set(entry.refs.get() + 1);
+                    } else {
+                        let _ = sender.send(Err(BlobURLStoreError::InvalidOrigin));
+                        return;
+                    }
+                },
+                None => {
+                    let _ = sender.send(Err(BlobURLStoreError::InvalidFileID));
+                    return;
+                }
+            };
+
+            let new_id = Uuid::new_v4();
+            self.store.insert(new_id, FileStoreEntry {
+                origin: origin_in.clone(),
+                file_impl: FileImpl::Sliced(id, rel_pos),
+                refs: Cell::new(1),
+            });
+
+            let _ = sender.send(Ok(SelectedFileId(new_id.simple().to_string())));
+        } else {
+            let _ = sender.send(Err(BlobURLStoreError::InvalidFileID));
+        }
+    }
+
     fn select_file(&mut self, patterns: Vec<FilterPattern>,
-                   sender: IpcSender<FileManagerResult<SelectedFile>>) {
+                   sender: IpcSender<FileManagerResult<SelectedFile>>,
+                   origin: FileOrigin) {
         match self.ui.open_file_dialog("", patterns) {
             Some(s) => {
                 let selected_path = Path::new(&s);
 
-                match self.create_entry(selected_path) {
+                match self.create_entry(selected_path, &origin) {
                     Some(triple) => { let _ = sender.send(Ok(triple)); }
                     None => { let _ = sender.send(Err(FileManagerThreadError::InvalidSelection)); }
                 };
             }
             None => {
                 let _ = sender.send(Err(FileManagerThreadError::UserCancelled));
                 return;
             }
         }
     }
 
     fn select_files(&mut self, patterns: Vec<FilterPattern>,
-                    sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>) {
+                    sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
+                    origin: FileOrigin) {
         match self.ui.open_file_dialog_multi("", patterns) {
             Some(v) => {
                 let mut selected_paths = vec![];
 
                 for s in &v {
                     selected_paths.push(Path::new(s));
                 }
 
                 let mut replies = vec![];
 
                 for path in selected_paths {
-                    match self.create_entry(path) {
+                    match self.create_entry(path, &origin) {
                         Some(triple) => replies.push(triple),
                         None => { let _ = sender.send(Err(FileManagerThreadError::InvalidSelection)); }
                     };
                 }
 
                 let _ = sender.send(Ok(replies));
             }
             None => {
                 let _ = sender.send(Err(FileManagerThreadError::UserCancelled));
                 return;
             }
         }
     }
 
-    fn create_entry(&mut self, file_path: &Path) -> Option<SelectedFile> {
+    fn create_entry(&mut self, file_path: &Path, origin: &str) -> Option<SelectedFile> {
         match File::open(file_path) {
             Ok(handler) => {
                 let id = Uuid::new_v4();
-                self.idmap.insert(id, file_path.to_path_buf());
+                let file_impl = FileImpl::PathOnly(file_path.to_path_buf());
+
+                self.store.insert(id, FileStoreEntry {
+                    origin: origin.to_string(),
+                    file_impl: file_impl,
+                    refs: Cell::new(1),
+                });
 
                 // Unix Epoch: https://doc.servo.org/std/time/constant.UNIX_EPOCH.html
                 let epoch = handler.metadata().and_then(|metadata| metadata.modified()).map_err(|_| ())
                                               .and_then(|systime| systime.elapsed().map_err(|_| ()))
                                               .and_then(|elapsed| {
                                                     let secs = elapsed.as_secs();
                                                     let nsecs = elapsed.subsec_nanos();
                                                     let msecs = secs * 1000 + nsecs as u64 / 1000000;
@@ -210,84 +298,143 @@ impl<UI: 'static + UIProvider> FileManag
                     }
                     _ => None
                 }
             },
             Err(_) => None
         }
     }
 
-    fn try_read_file(&mut self, id: SelectedFileId) -> Result<Vec<u8>, ()> {
+    fn try_read_file(&self, id: SelectedFileId, origin_in: String) -> Result<Vec<u8>, ()> {
         let id = try!(Uuid::parse_str(&id.0).map_err(|_| ()));
 
-        match self.idmap.get(&id) {
-            Some(filepath) => {
-                let mut buffer = vec![];
-                let mut handler = try!(File::open(&filepath).map_err(|_| ()));
-                try!(handler.read_to_end(&mut buffer).map_err(|_| ()));
-                Ok(buffer)
+        match self.store.get(&id) {
+            Some(entry) => {
+                match entry.file_impl {
+                    FileImpl::PathOnly(ref filepath) => {
+                        if *entry.origin == origin_in {
+                            let mut buffer = vec![];
+                            let mut handler = try!(File::open(filepath).map_err(|_| ()));
+                            try!(handler.read_to_end(&mut buffer).map_err(|_| ()));
+                            Ok(buffer)
+                        } else {
+                            Err(())
+                        }
+                    },
+                    FileImpl::Memory(ref buffered) => {
+                        Ok(buffered.bytes.clone())
+                    },
+                    FileImpl::Sliced(ref id, ref _rel_pos) => {
+                        self.try_read_file(SelectedFileId(id.simple().to_string()), origin_in)
+                    }
+                }
             },
-            None => Err(())
+            None => Err(()),
         }
     }
 
-    fn delete_fileid(&mut self, id: SelectedFileId) {
-        if let Ok(id) = Uuid::parse_str(&id.0) {
-            self.idmap.remove(&id);
-        }
-    }
-}
+    fn dec_ref(&mut self, id: Uuid, origin_in: FileOrigin) {
+        let (is_last_ref, opt_parent_id) = match self.store.get(&id) {
+            Some(entry) => {
+                if *entry.origin == origin_in {
+                    let r = entry.refs.get();
 
-pub struct BlobURLStore {
-    entries: HashMap<Uuid, (Origin, BlobURLStoreEntry)>,
-}
+                    if r > 1 {
+                        entry.refs.set(r - 1);
+                        (false, None)
+                    } else {
+                        if let FileImpl::Sliced(ref parent_id, _) = entry.file_impl {
+                            // if it has a reference to parent id, dec_ref on parent later
+                            (true, Some(parent_id.clone()))
+                        } else {
+                            (true, None)
+                        }
+                    }
+                } else { // Invalid origin
+                    return;
+                }
+            }
+            None => return, // Invalid UUID
+        };
 
-impl BlobURLStore {
-    pub fn new() -> BlobURLStore {
-        BlobURLStore {
-            entries: HashMap::new(),
+        if is_last_ref {
+            self.store.remove(&id);
+
+            if let Some(parent_id) = opt_parent_id {
+                self.dec_ref(parent_id, origin_in);
+            }
         }
     }
 
-    fn process(&mut self, msg: BlobURLStoreMsg) {
-        match msg {
-            BlobURLStoreMsg::AddEntry(entry, origin_str, sender) => {
-                match Url::parse(&origin_str) {
-                    Ok(base_url) => {
-                        let id = Uuid::new_v4();
-                        self.add_entry(id, base_url.origin(), entry);
+    fn process_request(&self, load_data: &LoadData, consumer: LoadConsumer,
+                       rel_pos: &RelativePos, id: &Uuid) {
+        let origin_in = load_data.url.origin().unicode_serialization();
+        match self.store.get(id) {
+            Some(entry) => {
+                match entry.file_impl {
+                    FileImpl::Memory(ref buffered) => {
+                        if *entry.origin == origin_in {
+                            load_blob(&load_data, consumer, self.classifier.clone(),
+                                      None, rel_pos, buffered);
+                        } else {
+                            let e = format!("Invalid blob URL origin {:?}", origin_in);
+                            send_error(load_data.url.clone(), NetworkError::Internal(e), consumer);
+                        }
+                    },
+                    FileImpl::PathOnly(ref filepath) => {
+                        let opt_filename = filepath.file_name()
+                                               .and_then(|osstr| osstr.to_str())
+                                               .map(|s| s.to_string());
 
-                        let _ = sender.send(Ok(id.simple().to_string()));
-                    }
-                    Err(_) => {
-                        let _ = sender.send(Err(BlobURLStoreError::InvalidOrigin));
+                        if *entry.origin == origin_in {
+                            let mut bytes = vec![];
+                            let mut handler = File::open(filepath).unwrap();
+                            let mime = guess_mime_type_opt(filepath);
+                            let size = handler.read_to_end(&mut bytes).unwrap();
+
+                            let entry = BlobURLStoreEntry {
+                                type_string: match mime {
+                                    Some(x) => format!("{}", x),
+                                    None    => "".to_string(),
+                                },
+                                size: size as u64,
+                                bytes: bytes,
+                            };
+
+                            load_blob(&load_data, consumer, self.classifier.clone(),
+                                      opt_filename, rel_pos, &entry);
+                        } else {
+                            let e = format!("Invalid blob URL origin {:?}", origin_in);
+                            send_error(load_data.url.clone(), NetworkError::Internal(e), consumer);
+                        }
+                    },
+                    FileImpl::Sliced(ref id, ref rel_pos) => {
+                        self.process_request(load_data, consumer, rel_pos, id);
                     }
                 }
             }
-            BlobURLStoreMsg::DeleteEntry(id) => {
-                if let Ok(id) = Uuid::parse_str(&id) {
-                    self.delete_entry(id);
-                }
-            },
+            _ => {
+                let e = format!("Invalid blob URL key {:?}", id.simple().to_string());
+                send_error(load_data.url.clone(), NetworkError::Internal(e), consumer);
+            }
         }
     }
 
-    pub fn request(&self, id: Uuid, origin: &Origin) -> Result<&BlobURLStoreEntry, BlobURLStoreError> {
-        match self.entries.get(&id) {
-            Some(ref pair) => {
-                if pair.0 == *origin {
-                    Ok(&pair.1)
-                } else {
-                    Err(BlobURLStoreError::InvalidOrigin)
-                }
+    fn transfer_memory(&mut self, entry: BlobURLStoreEntry, rel_pos: RelativePos,
+                       sender: IpcSender<Result<SelectedFileId, BlobURLStoreError>>, origin: FileOrigin) {
+        match Url::parse(&origin) { // parse to check sanity
+            Ok(_) => {
+                let id = Uuid::new_v4();
+                self.store.insert(id, FileStoreEntry {
+                    origin: origin.clone(),
+                    file_impl: FileImpl::Memory(entry),
+                    refs: Cell::new(1),
+                });
+                let sliced_id = SelectedFileId(id.simple().to_string());
+
+                self.add_sliced_entry(sliced_id, rel_pos, sender, origin);
             }
-            None => Err(BlobURLStoreError::InvalidKey)
+            Err(_) => {
+                let _ = sender.send(Err(BlobURLStoreError::InvalidOrigin));
+            }
         }
     }
-
-    pub fn add_entry(&mut self, id: Uuid, origin: Origin, blob: BlobURLStoreEntry) {
-        self.entries.insert(id, (origin, blob));
-    }
-
-    pub fn delete_entry(&mut self, id: Uuid) {
-        self.entries.remove(&id);
-    }
 }
--- a/servo/components/net_traits/Cargo.toml
+++ b/servo/components/net_traits/Cargo.toml
@@ -13,14 +13,15 @@ util = {path = "../util"}
 msg = {path = "../msg"}
 ipc-channel = {git = "https://github.com/servo/ipc-channel"}
 heapsize = "0.3.0"
 heapsize_plugin = "0.1.2"
 hyper = { version = "0.9.9", features = [ "serde-serialization" ] }
 image = "0.10"
 lazy_static = "0.2"
 log = "0.3.5"
+num-traits = "0.1.32"
 serde = "0.7.11"
 serde_macros = "0.7.11"
 url = {version = "1.0.0", features = ["heap_size"]}
 websocket = "0.17"
 uuid = { version = "0.2.2", features = ["v4", "serde"] }
 cookie = { version = "0.2.5", features = ["serialize-serde", "serialize-rustc" ] }
--- a/servo/components/net_traits/blob_url_store.rs
+++ b/servo/components/net_traits/blob_url_store.rs
@@ -1,43 +1,30 @@
 /* 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 ipc_channel::ipc::IpcSender;
 use std::str::FromStr;
 use url::Url;
 use uuid::Uuid;
 
 /// Errors returns to BlobURLStoreMsg::Request
-#[derive(Clone, Serialize, Deserialize)]
+#[derive(Clone, Debug, Serialize, Deserialize)]
 pub enum BlobURLStoreError {
-    /// Invalid UUID key
-    InvalidKey,
+    /// Invalid File UUID
+    InvalidFileID,
     /// Invalid URL origin
     InvalidOrigin,
 }
 
-#[derive(Serialize, Deserialize)]
-pub enum BlobURLStoreMsg {
-    /// Add an entry and send back the associated uuid
-    /// XXX: Second field is an unicode-serialized Origin, it is a temporary workaround
-    ///      and should not be trusted. See issue https://github.com/servo/servo/issues/11722
-    AddEntry(BlobURLStoreEntry, String, IpcSender<Result<String, BlobURLStoreError>>),
-    /// Delete an entry by uuid
-    DeleteEntry(String),
-}
-
 /// Blob URL store entry, a packaged form of Blob DOM object
 #[derive(Clone, Serialize, Deserialize)]
 pub struct BlobURLStoreEntry {
     /// MIME type string
     pub type_string: String,
-    /// Some filename if the backend of Blob is a file
-    pub filename: Option<String>,
     /// Size of content in bytes
     pub size: u64,
     /// Content of blob
     pub bytes: Vec<u8>,
 }
 
 /// Parse URL as Blob URL scheme's definition
 /// https://w3c.github.io/FileAPI/#DefinitionOfScheme
--- a/servo/components/net_traits/filemanager_thread.rs
+++ b/servo/components/net_traits/filemanager_thread.rs
@@ -1,17 +1,108 @@
 /* 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 blob_url_store::BlobURLStoreMsg;
+use blob_url_store::{BlobURLStoreEntry, BlobURLStoreError};
 use ipc_channel::ipc::IpcSender;
+use num_traits::ToPrimitive;
+use std::cmp::{max, min};
+use std::ops::Range;
 use std::path::PathBuf;
 use super::{LoadConsumer, LoadData};
 
+// HACK: We should send Origin directly instead of this in future, blocked on #11722
+/// File manager store entry's origin
+pub type FileOrigin = String;
+
+/// Relative slice positions of a sequence,
+/// whose semantic should be consistent with (start, end) parameters in
+/// https://w3c.github.io/FileAPI/#dfn-slice
+#[derive(Clone, Deserialize, Serialize)]
+pub struct RelativePos {
+    /// Relative to first byte if non-negative,
+    /// relative to one past last byte if negative,
+    pub start: i64,
+    /// Relative offset from first byte if Some(non-negative),
+    /// relative to one past last byte if Some(negative),
+    /// None if one past last byte
+    pub end: Option<i64>,
+}
+
+impl RelativePos {
+    /// Full range from start to end
+    pub fn full_range() -> RelativePos {
+        RelativePos {
+            start: 0,
+            end: Some(0),
+        }
+    }
+
+    /// Instantiate optional slice position parameters
+    pub fn from_opts(start: Option<i64>, end: Option<i64>) -> RelativePos {
+        RelativePos {
+            start: start.unwrap_or(0),
+            end: end,
+        }
+    }
+
+    /// Slice the inner sliced range by repositioning
+    pub fn slice_inner(&self, rel_pos: &RelativePos) -> RelativePos {
+        RelativePos {
+            start: self.start + rel_pos.start,
+            end: match (self.end, rel_pos.end) {
+                (Some(old_end), Some(rel_end)) => Some(old_end + rel_end),
+                (old, None) => old,
+                (None, rel) => rel,
+            }
+        }
+    }
+
+    /// Compute absolute range by giving the total size
+    /// https://w3c.github.io/FileAPI/#slice-method-algo
+    pub fn to_abs_range(&self, size: usize) -> Range<usize> {
+        let size = size as i64;
+
+        let start = {
+            if self.start < 0 {
+                max(size + self.start, 0)
+            } else {
+                min(self.start, size)
+            }
+        };
+
+        let end = match self.end {
+            Some(rel_end) => {
+                if rel_end < 0 {
+                    max(size + rel_end, 0)
+                } else {
+                    min(rel_end, size)
+                }
+            }
+            None => size,
+        };
+
+        let span: i64 = max(end - start, 0);
+
+        Range {
+            start: start.to_usize().unwrap(),
+            end: (start + span).to_usize().unwrap(),
+        }
+    }
+
+    /// Inverse operation of to_abs_range
+    pub fn from_abs_range(range: Range<usize>, size: usize) -> RelativePos {
+        RelativePos {
+            start: range.start as i64,
+            end: Some(size as i64 - range.end as i64),
+        }
+    }
+}
+
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct SelectedFileId(pub String);
 
 #[derive(Debug, Deserialize, Serialize)]
 pub struct SelectedFile {
     pub id: SelectedFileId,
     pub filename: PathBuf,
     pub modified: u64,
@@ -22,33 +113,39 @@ pub struct SelectedFile {
 /// Filter for file selection
 /// the content is expected to be extension (e.g, "doc", without the prefixing ".")
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct FilterPattern(pub String);
 
 #[derive(Deserialize, Serialize)]
 pub enum FileManagerThreadMsg {
     /// Select a single file, return triple (FileID, FileName, lastModified)
-    SelectFile(Vec<FilterPattern>, IpcSender<FileManagerResult<SelectedFile>>),
+    SelectFile(Vec<FilterPattern>, IpcSender<FileManagerResult<SelectedFile>>, FileOrigin),
 
     /// Select multiple files, return a vector of triples
-    SelectFiles(Vec<FilterPattern>, IpcSender<FileManagerResult<Vec<SelectedFile>>>),
+    SelectFiles(Vec<FilterPattern>, IpcSender<FileManagerResult<Vec<SelectedFile>>>, FileOrigin),
 
     /// Read file, return the bytes
-    ReadFile(IpcSender<FileManagerResult<Vec<u8>>>, SelectedFileId),
-
-    /// Delete the FileID entry
-    DeleteFileID(SelectedFileId),
-
-    // Blob URL message
-    BlobURLStoreMsg(BlobURLStoreMsg),
+    ReadFile(IpcSender<FileManagerResult<Vec<u8>>>, SelectedFileId, FileOrigin),
 
     /// Load resource by Blob URL
     LoadBlob(LoadData, LoadConsumer),
 
+    /// Add an entry and send back the associated uuid
+    TransferMemory(BlobURLStoreEntry, RelativePos, IpcSender<Result<SelectedFileId, BlobURLStoreError>>, FileOrigin),
+
+    /// Add a sliced entry pointing to the parent id with a relative slicing positing
+    AddSlicedEntry(SelectedFileId, RelativePos, IpcSender<Result<SelectedFileId, BlobURLStoreError>>, FileOrigin),
+
+    /// Decrease reference count
+    DecRef(SelectedFileId, FileOrigin),
+
+    /// Increase reference count
+    IncRef(SelectedFileId, FileOrigin),
+
     /// Shut down this thread
     Exit,
 }
 
 pub type FileManagerResult<T> = Result<T, FileManagerThreadError>;
 
 #[derive(Debug, Deserialize, Serialize)]
 pub enum FileManagerThreadError {
--- a/servo/components/net_traits/lib.rs
+++ b/servo/components/net_traits/lib.rs
@@ -18,16 +18,17 @@ extern crate hyper;
 extern crate image as piston_image;
 extern crate ipc_channel;
 #[allow(unused_extern_crates)]
 #[macro_use]
 extern crate lazy_static;
 #[macro_use]
 extern crate log;
 extern crate msg;
+extern crate num_traits;
 extern crate serde;
 extern crate url;
 extern crate util;
 extern crate uuid;
 extern crate websocket;
 
 use cookie_rs::Cookie;
 use filemanager_thread::FileManagerThreadMsg;
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -52,17 +52,17 @@ use hyper::method::Method;
 use hyper::mime::Mime;
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
 use js::glue::{CallObjectTracer, CallUnbarrieredObjectTracer, CallValueTracer};
 use js::jsapi::{GCTraceKindToAscii, Heap, TraceKind, JSObject, JSTracer};
 use js::jsval::JSVal;
 use js::rust::Runtime;
 use libc;
 use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeData, WindowSizeType, ReferrerPolicy};
-use net_traits::filemanager_thread::SelectedFileId;
+use net_traits::filemanager_thread::{SelectedFileId, RelativePos};
 use net_traits::image::base::{Image, ImageMetadata};
 use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
 use net_traits::response::HttpsState;
 use net_traits::storage_thread::StorageType;
 use net_traits::{Metadata, NetworkError, ResourceThreads};
 use offscreen_gl_context::GLLimits;
 use profile_traits::mem::ProfilerChan as MemProfilerChan;
 use profile_traits::time::ProfilerChan as TimeProfilerChan;
@@ -326,16 +326,17 @@ no_jsmanaged_fields!(ElementSnapshot);
 no_jsmanaged_fields!(HttpsState);
 no_jsmanaged_fields!(SharedRt);
 no_jsmanaged_fields!(TouchpadPressurePhase);
 no_jsmanaged_fields!(USVString);
 no_jsmanaged_fields!(ReferrerPolicy);
 no_jsmanaged_fields!(ResourceThreads);
 no_jsmanaged_fields!(SystemTime);
 no_jsmanaged_fields!(SelectedFileId);
+no_jsmanaged_fields!(RelativePos);
 no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
 no_jsmanaged_fields!(CSSErrorReporter);
 no_jsmanaged_fields!(WebGLBufferId);
 no_jsmanaged_fields!(WebGLFramebufferId);
 no_jsmanaged_fields!(WebGLProgramId);
 no_jsmanaged_fields!(WebGLRenderbufferId);
 no_jsmanaged_fields!(WebGLShaderId);
 no_jsmanaged_fields!(WebGLTextureId);
--- a/servo/components/script/dom/blob.rs
+++ b/servo/components/script/dom/blob.rs
@@ -3,69 +3,46 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::BlobBinding;
 use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
 use dom::bindings::codegen::UnionTypes::BlobOrString;
 use dom::bindings::error::{Error, Fallible};
 use dom::bindings::global::GlobalRef;
-use dom::bindings::js::Root;
+use dom::bindings::js::{JS, Root};
 use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use encoding::all::UTF_8;
 use encoding::types::{EncoderTrap, Encoding};
 use ipc_channel::ipc;
-use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId};
-use num_traits::ToPrimitive;
+use net_traits::IpcSend;
+use net_traits::blob_url_store::BlobURLStoreEntry;
+use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId, RelativePos};
 use std::ascii::AsciiExt;
 use std::cell::Cell;
-use std::cmp::{max, min};
+use std::ops::Range;
 use std::sync::Arc;
 
 #[derive(Clone, JSTraceable)]
 pub struct DataSlice {
     bytes: Arc<Vec<u8>>,
     bytes_start: usize,
     bytes_end: usize
 }
 
 impl DataSlice {
     /// Construct DataSlice from reference counted bytes
     pub fn new(bytes: Arc<Vec<u8>>, start: Option<i64>, end: Option<i64>) -> DataSlice {
-        let size = bytes.len() as i64;
-        let relativeStart: i64 = match start {
-            None => 0,
-            Some(start) => {
-                if start < 0 {
-                    max(size + start, 0)
-                } else {
-                    min(start, size)
-                }
-            }
-        };
-        let relativeEnd: i64 = match end {
-            None => size,
-            Some(end) => {
-                if end < 0 {
-                    max(size + end, 0)
-                } else {
-                    min(end, size)
-                }
-            }
-        };
-
-        let span: i64 = max(relativeEnd - relativeStart, 0);
-        let start = relativeStart.to_usize().unwrap();
-        let end = (relativeStart + span).to_usize().unwrap();
+        let range = RelativePos::from_opts(start, end).to_abs_range(bytes.len());
 
         DataSlice {
             bytes: bytes,
-            bytes_start: start,
-            bytes_end: end
+            bytes_start: range.start,
+            bytes_end: range.end,
         }
     }
 
     /// Construct data slice from a vector of bytes
     pub fn from_bytes(bytes: Vec<u8>) -> DataSlice {
         DataSlice::new(Arc::new(bytes), None, None)
     }
 
@@ -82,25 +59,40 @@ impl DataSlice {
     pub fn get_bytes(&self) -> &[u8] {
         &self.bytes[self.bytes_start..self.bytes_end]
     }
 
     /// Get length of sliced bytes
     pub fn size(&self) -> u64 {
         (self.bytes_end as u64) - (self.bytes_start as u64)
     }
+
+    /// Further adjust the slice range based on passed-in relative positions
+    pub fn slice(&self, pos: &RelativePos) -> DataSlice {
+        let old_size = self.size();
+        let range = pos.to_abs_range(old_size as usize);
+        DataSlice {
+            bytes: self.bytes.clone(),
+            bytes_start: self.bytes_start + range.start,
+            bytes_end: self.bytes_start + range.end,
+        }
+    }
 }
 
-
-#[derive(Clone, JSTraceable)]
+#[must_root]
+#[derive(JSTraceable)]
 pub enum BlobImpl {
-    /// File-based, cached backend
+    /// File-based blob, including id and possibly cached content
     File(SelectedFileId, DOMRefCell<Option<DataSlice>>),
-    /// Memory-based backend
+    /// Memory-based blob
     Memory(DataSlice),
+    /// Sliced blob, including parent blob and
+    /// relative positions representing current slicing range,
+    /// it is leaf of a two-layer fat tree
+    Sliced(JS<Blob>, RelativePos),
 }
 
 impl BlobImpl {
     /// Construct memory-backed BlobImpl from DataSlice
     pub fn new_from_slice(slice: DataSlice) -> BlobImpl {
         BlobImpl::Memory(slice)
     }
 
@@ -115,36 +107,68 @@ impl BlobImpl {
     }
 }
 
 // https://w3c.github.io/FileAPI/#blob
 #[dom_struct]
 pub struct Blob {
     reflector_: Reflector,
     #[ignore_heap_size_of = "No clear owner"]
-    blob_impl: BlobImpl,
+    blob_impl: DOMRefCell<BlobImpl>,
     typeString: String,
     isClosed_: Cell<bool>,
 }
 
 impl Blob {
+    #[allow(unrooted_must_root)]
     pub fn new(global: GlobalRef, blob_impl: BlobImpl, typeString: String) -> Root<Blob> {
         let boxed_blob = box Blob::new_inherited(blob_impl, typeString);
         reflect_dom_object(boxed_blob, global, BlobBinding::Wrap)
     }
 
+    #[allow(unrooted_must_root)]
     pub fn new_inherited(blob_impl: BlobImpl, typeString: String) -> Blob {
         Blob {
             reflector_: Reflector::new(),
-            blob_impl: blob_impl,
+            blob_impl: DOMRefCell::new(blob_impl),
             typeString: typeString,
             isClosed_: Cell::new(false),
         }
     }
 
+    #[allow(unrooted_must_root)]
+    fn new_sliced(parent: &Blob, rel_pos: RelativePos,
+                  relativeContentType: DOMString) -> Root<Blob> {
+        let global = parent.global();
+        let blob_impl = match *parent.blob_impl.borrow() {
+            BlobImpl::File(ref id, _) => {
+                inc_ref_id(global.r(), id.clone());
+
+                // Create new parent node
+                BlobImpl::Sliced(JS::from_ref(parent), rel_pos)
+            }
+            BlobImpl::Memory(_) => {
+                // Create new parent node
+                BlobImpl::Sliced(JS::from_ref(parent), rel_pos)
+            }
+            BlobImpl::Sliced(ref grandparent, ref old_rel_pos) => {
+                // Adjust the slicing position, using same parent
+                let new_rel_pos = old_rel_pos.slice_inner(&rel_pos);
+
+                if let BlobImpl::File(ref id, _) = *grandparent.blob_impl.borrow() {
+                    inc_ref_id(global.r(), id.clone());
+                }
+
+                BlobImpl::Sliced(grandparent.clone(), new_rel_pos)
+            }
+        };
+
+        Blob::new(global.r(), blob_impl, relativeContentType.into())
+    }
+
     // https://w3c.github.io/FileAPI/#constructorBlob
     pub fn Constructor(global: GlobalRef,
                        blobParts: Option<Vec<BlobOrString>>,
                        blobPropertyBag: &BlobBinding::BlobPropertyBag)
                        -> Fallible<Root<Blob>> {
         // TODO: accept other blobParts types - ArrayBuffer or ArrayBufferView
         let bytes: Vec<u8> = match blobParts {
             None => Vec::new(),
@@ -155,42 +179,123 @@ impl Blob {
         };
 
         let slice = DataSlice::from_bytes(bytes);
         Ok(Blob::new(global, BlobImpl::new_from_slice(slice), blobPropertyBag.get_typestring()))
     }
 
     /// Get a slice to inner data, this might incur synchronous read and caching
     pub fn get_slice(&self) -> Result<DataSlice, ()> {
-        match self.blob_impl {
-            BlobImpl::File(ref id, ref slice) => {
-                match *slice.borrow() {
+        match *self.blob_impl.borrow() {
+            BlobImpl::File(ref id, ref cached) => {
+                let buffer = match *cached.borrow() {
                     Some(ref s) => Ok(s.clone()),
                     None => {
                         let global = self.global();
                         let s = read_file(global.r(), id.clone())?;
-                        *slice.borrow_mut() = Some(s.clone()); // Cached
                         Ok(s)
                     }
+                };
+
+                // Cache
+                if let Ok(buf) = buffer.clone() {
+                    *cached.borrow_mut() = Some(buf);
                 }
+
+                buffer
             }
-            BlobImpl::Memory(ref s) => Ok(s.clone())
+            BlobImpl::Memory(ref s) => Ok(s.clone()),
+            BlobImpl::Sliced(ref parent, ref rel_pos) => {
+                let dataslice = parent.get_slice_or_empty();
+                Ok(dataslice.slice(rel_pos))
+            }
         }
     }
 
     /// Try to get a slice, and if any exception happens, return the empty slice
     pub fn get_slice_or_empty(&self) -> DataSlice {
         self.get_slice().unwrap_or(DataSlice::empty())
     }
+
+    pub fn get_id(&self) -> SelectedFileId {
+        match *self.blob_impl.borrow() {
+            BlobImpl::File(ref id, _) => id.clone(),
+            BlobImpl::Memory(ref slice) => self.promote_to_file(slice),
+            BlobImpl::Sliced(ref parent, ref rel_pos) => {
+                match *parent.blob_impl.borrow() {
+                    BlobImpl::Sliced(_, _) => {
+                        debug!("Sliced can't have a sliced parent");
+                        // Return dummy id
+                        SelectedFileId("".to_string())
+                    }
+                    BlobImpl::File(ref parent_id, _) =>
+                        self.create_sliced_id(parent_id, rel_pos),
+                    BlobImpl::Memory(ref parent_slice) => {
+                        let parent_id = parent.promote_to_file(parent_slice);
+                        *self.blob_impl.borrow_mut() = BlobImpl::Sliced(parent.clone(), rel_pos.clone());
+                        self.create_sliced_id(&parent_id, rel_pos)
+                    }
+                }
+            }
+        }
+    }
+
+    /// Promite memory-based Blob to file-based,
+    /// The bytes in data slice will be transferred to file manager thread
+    fn promote_to_file(&self, self_slice: &DataSlice) -> SelectedFileId {
+        let global = self.global();
+        let origin = global.r().get_url().origin().unicode_serialization();
+        let filemanager = global.r().resource_threads().sender();
+        let bytes = self_slice.get_bytes();
+        let rel_pos = RelativePos::from_abs_range(Range {
+                            start: self_slice.bytes_start,
+                            end: self_slice.bytes_end,
+                        }, self_slice.bytes.len());
+
+        let entry = BlobURLStoreEntry {
+            type_string: self.typeString.clone(),
+            size: self.Size(),
+            bytes: bytes.to_vec(),
+        };
+
+        let (tx, rx) = ipc::channel().unwrap();
+        let _ = filemanager.send(FileManagerThreadMsg::TransferMemory(entry, rel_pos, tx, origin.clone()));
+
+        match rx.recv().unwrap() {
+            Ok(new_id) => SelectedFileId(new_id.0),
+            // Dummy id
+            Err(_) => SelectedFileId("".to_string()),
+        }
+    }
+
+    fn create_sliced_id(&self, parent_id: &SelectedFileId,
+                        rel_pos: &RelativePos) -> SelectedFileId {
+        let global = self.global();
+
+        let origin = global.r().get_url().origin().unicode_serialization();
+
+        let filemanager = global.r().resource_threads().sender();
+        let (tx, rx) = ipc::channel().unwrap();
+        let msg = FileManagerThreadMsg::AddSlicedEntry(parent_id.clone(),
+                                                       rel_pos.clone(),
+                                                       tx, origin.clone());
+        let _ = filemanager.send(msg);
+        let new_id = rx.recv().unwrap().unwrap();
+
+        // Return the indirect id reference
+        SelectedFileId(new_id.0)
+    }
 }
 
 fn read_file(global: GlobalRef, id: SelectedFileId) -> Result<DataSlice, ()> {
     let file_manager = global.filemanager_thread();
     let (chan, recv) = ipc::channel().map_err(|_|())?;
-    let _ = file_manager.send(FileManagerThreadMsg::ReadFile(chan, id));
+    let origin = global.get_url().origin().unicode_serialization();
+    let msg = FileManagerThreadMsg::ReadFile(chan, id, origin);
+    let _ = file_manager.send(msg);
 
     let result = match recv.recv() {
         Ok(ret) => ret,
         Err(e) => {
             debug!("File manager thread has problem {:?}", e);
             return Err(())
         }
     };
@@ -243,20 +348,18 @@ impl BlobMethods for Blob {
                     str.make_ascii_lowercase();
                     str
                 } else {
                     DOMString::new()
                 }
             }
         };
 
-        let global = self.global();
-        let bytes = self.get_slice_or_empty().bytes.clone();
-        let slice = DataSlice::new(bytes, start, end);
-        Blob::new(global.r(), BlobImpl::new_from_slice(slice), relativeContentType.into())
+        let rel_pos = RelativePos::from_opts(start, end);
+        Blob::new_sliced(self, rel_pos, relativeContentType)
     }
 
     // https://w3c.github.io/FileAPI/#dfn-isClosed
     fn IsClosed(&self) -> bool {
         self.isClosed_.get()
     }
 
     // https://w3c.github.io/FileAPI/#dfn-close
@@ -269,17 +372,16 @@ impl BlobMethods for Blob {
         // Step 2
         self.isClosed_.set(true);
 
         // TODO Step 3 if Blob URL Store is implemented
 
     }
 }
 
-
 impl BlobBinding::BlobPropertyBag {
     /// Get the normalized inner type string
     /// https://w3c.github.io/FileAPI/#dfn-type
     pub fn get_typestring(&self) -> String {
         if is_ascii_printable(&self.type_) {
             self.type_.to_lowercase()
         } else {
             "".to_string()
@@ -287,8 +389,16 @@ impl BlobBinding::BlobPropertyBag {
     }
 }
 
 fn is_ascii_printable(string: &str) -> bool {
     // Step 5.1 in Sec 5.1 of File API spec
     // https://w3c.github.io/FileAPI/#constructorBlob
     string.chars().all(|c| c >= '\x20' && c <= '\x7E')
 }
+
+/// Bump the reference counter in file manager thread
+fn inc_ref_id(global: GlobalRef, id: SelectedFileId) {
+    let file_manager = global.filemanager_thread();
+    let origin = global.get_url().origin().unicode_serialization();
+    let msg = FileManagerThreadMsg::IncRef(id, origin);
+    let _ = file_manager.send(msg);
+}
--- a/servo/components/script/dom/file.rs
+++ b/servo/components/script/dom/file.rs
@@ -18,32 +18,34 @@ use time;
 #[dom_struct]
 pub struct File {
     blob: Blob,
     name: DOMString,
     modified: i64,
 }
 
 impl File {
+    #[allow(unrooted_must_root)]
     fn new_inherited(blob_impl: BlobImpl, name: DOMString,
                      modified: Option<i64>, typeString: &str) -> File {
         File {
             blob: Blob::new_inherited(blob_impl, typeString.to_owned()),
             name: name,
             // https://w3c.github.io/FileAPI/#dfn-lastModified
             modified: match modified {
                 Some(m) => m,
                 None => {
                     let time = time::get_time();
                     time.sec * 1000 + (time.nsec / 1000000) as i64
                 }
             },
         }
     }
 
+    #[allow(unrooted_must_root)]
     pub fn new(global: GlobalRef, blob_impl: BlobImpl,
                name: DOMString, modified: Option<i64>, typeString: &str) -> Root<File> {
         reflect_dom_object(box File::new_inherited(blob_impl, name, modified, typeString),
                            global,
                            FileBinding::Wrap)
     }
 
     // Construct from selected file message from file manager thread
--- a/servo/components/script/dom/htmlinputelement.rs
+++ b/servo/components/script/dom/htmlinputelement.rs
@@ -1147,27 +1147,28 @@ impl Activatable for HTMLInputElement {
                                   EventCancelable::NotCancelable);
                 target.fire_event("change",
                                   EventBubbles::Bubbles,
                                   EventCancelable::NotCancelable);
             },
             InputType::InputFile => {
                 // https://html.spec.whatwg.org/multipage/#file-upload-state-(type=file)
                 let window = window_from_node(self);
+                let origin = window.get_url().origin().unicode_serialization();
                 let filemanager = window.resource_threads().sender();
 
                 let mut files: Vec<Root<File>> = vec![];
                 let mut error = None;
 
                 let filter = filter_from_accept(&self.Accept());
                 let target = self.upcast::<EventTarget>();
 
                 if self.Multiple() {
                     let (chan, recv) = ipc::channel().expect("Error initializing channel");
-                    let msg = FileManagerThreadMsg::SelectFiles(filter, chan);
+                    let msg = FileManagerThreadMsg::SelectFiles(filter, chan, origin);
                     let _ = filemanager.send(msg).unwrap();
 
                     match recv.recv().expect("IpcSender side error") {
                         Ok(selected_files) => {
                             for selected in selected_files {
                                 files.push(File::new_from_selected(window.r(), selected));
                             }
 
@@ -1177,17 +1178,17 @@ impl Activatable for HTMLInputElement {
                             target.fire_event("change",
                                               EventBubbles::Bubbles,
                                               EventCancelable::NotCancelable);
                         },
                         Err(err) => error = Some(err),
                     };
                 } else {
                     let (chan, recv) = ipc::channel().expect("Error initializing channel");
-                    let msg = FileManagerThreadMsg::SelectFile(filter, chan);
+                    let msg = FileManagerThreadMsg::SelectFile(filter, chan, origin);
                     let _ = filemanager.send(msg).unwrap();
 
                     match recv.recv().expect("IpcSender side error") {
                         Ok(selected) => {
                             files.push(File::new_from_selected(window.r(), selected));
 
                             target.fire_event("input",
                                               EventBubbles::Bubbles,
--- a/servo/components/script/dom/url.rs
+++ b/servo/components/script/dom/url.rs
@@ -8,20 +8,19 @@ use dom::bindings::codegen::Bindings::UR
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::global::GlobalRef;
 use dom::bindings::js::{JS, MutNullableHeap, Root};
 use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
 use dom::bindings::str::{DOMString, USVString};
 use dom::blob::Blob;
 use dom::urlhelper::UrlHelper;
 use dom::urlsearchparams::URLSearchParams;
-use ipc_channel::ipc;
 use net_traits::IpcSend;
-use net_traits::blob_url_store::{BlobURLStoreEntry, BlobURLStoreMsg, parse_blob_url};
-use net_traits::filemanager_thread::FileManagerThreadMsg;
+use net_traits::blob_url_store::parse_blob_url;
+use net_traits::filemanager_thread::{SelectedFileId, FileManagerThreadMsg};
 use std::borrow::ToOwned;
 use std::default::Default;
 use url::quirks::domain_to_unicode;
 use url::{Host, Url};
 use uuid::Uuid;
 
 // https://url.spec.whatwg.org/#url
 #[dom_struct]
@@ -120,64 +119,41 @@ impl URL {
         let origin = URL::get_blob_origin(&global.get_url());
 
         if blob.IsClosed() {
             // Generate a dummy id
             let id = Uuid::new_v4().simple().to_string();
             return DOMString::from(URL::unicode_serialization_blob_url(&origin, &id));
         }
 
-        let filemanager = global.resource_threads().sender();
-
-        let slice = blob.get_slice_or_empty();
-        let bytes = slice.get_bytes();
-
-        let entry = BlobURLStoreEntry {
-            type_string: blob.Type().to_string(),
-            filename: None, // XXX: the filename is currently only in File object now
-            size: blob.Size(),
-            bytes: bytes.to_vec(),
-        };
-
-        let (tx, rx) = ipc::channel().unwrap();
+        let id = blob.get_id();
 
-        let msg = BlobURLStoreMsg::AddEntry(entry, origin.clone(), tx);
-
-        let _ = filemanager.send(FileManagerThreadMsg::BlobURLStoreMsg(msg));
-
-        match rx.recv().unwrap() {
-            Ok(id) => {
-                DOMString::from(URL::unicode_serialization_blob_url(&origin, &id))
-            }
-            Err(_) => {
-                // Generate a dummy id
-                let id = Uuid::new_v4().simple().to_string();
-                DOMString::from(URL::unicode_serialization_blob_url(&origin, &id))
-            }
-        }
+        DOMString::from(URL::unicode_serialization_blob_url(&origin, &id.0))
     }
 
     // https://w3c.github.io/FileAPI/#dfn-revokeObjectURL
     pub fn RevokeObjectURL(global: GlobalRef, url: DOMString) {
         /*
             If the url refers to a Blob that has a readability state of CLOSED OR
             if the value provided for the url argument is not a Blob URL, OR
             if the value provided for the url argument does not have an entry in the Blob URL Store,
 
             this method call does nothing. User agents may display a message on the error console.
 
             NOTE: The first step is unnecessary, since closed blobs do not exist in the store
         */
+        let origin = global.get_url().origin().unicode_serialization();
 
         match Url::parse(&url) {
             Ok(url) => match parse_blob_url(&url) {
                 Some((id, _)) => {
                     let filemanager = global.resource_threads().sender();
-                    let msg = BlobURLStoreMsg::DeleteEntry(id.simple().to_string());
-                    let _ = filemanager.send(FileManagerThreadMsg::BlobURLStoreMsg(msg));
+                    let id = SelectedFileId(id.simple().to_string());
+                    let msg = FileManagerThreadMsg::DecRef(id, origin);
+                    let _ = filemanager.send(msg);
                 }
                 None => {}
             },
             Err(_) => {}
         }
     }
 
     // https://w3c.github.io/FileAPI/#unicodeSerializationOfBlobURL
--- a/servo/components/servo/Cargo.lock
+++ b/servo/components/servo/Cargo.lock
@@ -1473,16 +1473,17 @@ dependencies = [
  "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "image 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.2.3 (git+https://github.com/servo/ipc-channel)",
  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
+ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "uuid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "websocket 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
--- a/servo/ports/cef/Cargo.lock
+++ b/servo/ports/cef/Cargo.lock
@@ -1354,16 +1354,17 @@ dependencies = [
  "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_plugin 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "image 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.2.3 (git+https://github.com/servo/ipc-channel)",
  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
+ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "uuid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "websocket 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
--- a/servo/tests/unit/net/filemanager_thread.rs
+++ b/servo/tests/unit/net/filemanager_thread.rs
@@ -30,60 +30,60 @@ fn test_filemanager() {
     // Try to open a dummy file "tests/unit/net/test.txt" in tree
     let mut handler = File::open("test.txt").expect("test.txt is stolen");
     let mut test_file_content = vec![];
 
     handler.read_to_end(&mut test_file_content)
            .expect("Read tests/unit/net/test.txt error");
 
     let patterns = vec![FilterPattern(".txt".to_string())];
-
+    let origin = "test.com".to_string();
 
     {
         // Try to select a dummy file "tests/unit/net/test.txt"
         let (tx, rx) = ipc::channel().unwrap();
-        chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx)).unwrap();
+        chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone())).unwrap();
         let selected = rx.recv().expect("File manager channel is broken")
                                 .expect("The file manager failed to find test.txt");
 
         // Expecting attributes conforming the spec
         assert!(selected.filename == PathBuf::from("test.txt"));
         assert!(selected.type_string == "text/plain".to_string());
 
         // Test by reading, expecting same content
         {
             let (tx2, rx2) = ipc::channel().unwrap();
-            chan.send(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone())).unwrap();
+            chan.send(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), origin.clone())).unwrap();
 
             let msg = rx2.recv().expect("File manager channel is broken");
 
             let vec = msg.expect("File manager reading failure is unexpected");
             assert!(test_file_content == vec, "Read content differs");
         }
 
         // Delete the id
-        chan.send(FileManagerThreadMsg::DeleteFileID(selected.id.clone())).unwrap();
+        chan.send(FileManagerThreadMsg::DecRef(selected.id.clone(), origin.clone())).unwrap();
 
         // Test by reading again, expecting read error because we invalidated the id
         {
             let (tx2, rx2) = ipc::channel().unwrap();
-            chan.send(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone())).unwrap();
+            chan.send(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), origin.clone())).unwrap();
 
             let msg = rx2.recv().expect("File manager channel is broken");
 
             match msg {
                 Err(FileManagerThreadError::ReadFileError) => {},
                 other => {
                     assert!(false, "Get unexpected response after deleting the id: {:?}", other);
                 }
             }
         }
     }
 
     let _ = chan.send(FileManagerThreadMsg::Exit);
 
     {
         let (tx, rx) = ipc::channel().unwrap();
-        let _ = chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx));
+        let _ = chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone()));
 
         assert!(rx.try_recv().is_err(), "The thread should not respond normally after exited");
     }
 }