CSC/ECE 517 Spring 2017/M1702 Implement the Mutation Observer API: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
(→‎Project Description: updated the code snippets to the latest submitted and merged version)
 
(9 intermediate revisions by 2 users not shown)
Line 25: Line 25:
     [Constructor(MutationCallback callback)]
     [Constructor(MutationCallback callback)]
     interface MutationObserver {
     interface MutationObserver {
       void observe(Node target, optional MutationObserverInit options);
       //void observe(Node target, optional MutationObserverInit options);
       void disconnect();
       //void disconnect();
       sequence<MutationRecord> takeRecords();
       //sequence<MutationRecord> takeRecords();
     };
     };
      
      
Line 47: Line 47:
       readonly attribute DOMString type;
       readonly attribute DOMString type;
       [SameObject] readonly attribute Node target;
       [SameObject] readonly attribute Node target;
       [SameObject] readonly attribute NodeList addedNodes;
       //[SameObject] readonly attribute NodeList addedNodes;
       [SameObject] readonly attribute NodeList removedNodes;
       //[SameObject] readonly attribute NodeList removedNodes;
       readonly attribute Node? previousSibling;
       //readonly attribute Node? previousSibling;
       readonly attribute Node? nextSibling;
       //readonly attribute Node? nextSibling;
       readonly attribute DOMString? attributeName;
       //readonly attribute DOMString? attributeName;
       readonly attribute DOMString? attributeNamespace;
       //readonly attribute DOMString? attributeNamespace;
       readonly attribute DOMString? oldValue;
       //readonly attribute DOMString? oldValue;
     };
     };
*** Added the new interfaces in the dom scripts folder:
*** Added the new interfaces in the dom scripts folder.
**mutationobserver.rs contains the implementation of the MutationObserver IDL interface.
     '''components/script/dom/mutationobserver.rs'''
     '''components/script/dom/mutationobserver.rs'''
     use core::ptr::null;
     use dom::bindings::codegen::Bindings::MutationObserverBinding;
    use dom;
     use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationCallback;
     use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationCallback;
    use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserverInit;
     use dom::bindings::error::Fallible;
     use dom::bindings::error::Error;
     use dom::bindings::js::Root;
     use dom::bindings::js::Root;
     use dom::bindings::reflector::Reflector;
     use dom::bindings::reflector::{Reflector, reflect_dom_object};
    use dom::bindings::trace::JSTraceable;
    use dom::mutationrecord::MutationRecord;
    use dom::node::Node;
     use dom::window::Window;
     use dom::window::Window;
     use dom_struct::dom_struct;
     use dom_struct::dom_struct;
     use script_thread::ScriptThread;
     use script_thread::ScriptThread;
    use std::ops::Deref;
     use std::rc::Rc;
     use std::rc::Rc;
      
      
Line 76: Line 71:
     pub struct MutationObserver {
     pub struct MutationObserver {
         reflector_: Reflector,
         reflector_: Reflector,
         callback: MutationCallback,
        #[ignore_heap_size_of = "can't measure Rc values"]
         callback: Rc<MutationCallback>,
     }
     }
      
      
     impl MutationObserver {
     impl MutationObserver {
         fn new(global: &Window, callback: MutationCallback) -> MutationObserver {
         fn new(global: &Window, callback: Rc<MutationCallback>) -> Root<MutationObserver> {
            let boxed_observer = box MutationObserver::new_inherited(callback);
            reflect_dom_object(boxed_observer, global, MutationObserverBinding::Wrap)
        }
       
        fn new_inherited(callback: Rc<MutationCallback>) -> MutationObserver {
             MutationObserver {
             MutationObserver {
                 reflector_: Reflector::new(),
                 reflector_: Reflector::new(),
Line 86: Line 87:
             }
             }
         }
         }
   
       
         pub fn Constructor(global: &Window, callback: Rc<MutationCallback>) -> Result<Root<MutationObserver>, Error> {
         pub fn Constructor(global: &Window, callback: Rc<MutationCallback>) -> Fallible<Root<MutationObserver>> {
             let observer = MutationObserver::new(global, Rc::deref(&callback));
             let observer = MutationObserver::new(global, callback);
             ScriptThread::add_mutation_observer(&observer)
             ScriptThread::add_mutation_observer(&*observer);
            Ok(observer)
         }
         }
   
     }
     }
**mutationrecord.rs contains the implementation of the MutationRecord IDL interface. It also contains the 'getter' functions for the private methods.
     '''components/script/dom/mutationrecord.rs'''
     '''components/script/dom/mutationrecord.rs'''
    use core::ptr;
     use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding::MutationRecordMethods;
     use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding::MutationRecordMethods;
     use dom::bindings::js::{JS, Root};
     use dom::bindings::js::{JS, Root};
Line 100: Line 101:
     use dom::bindings::str::DOMString;
     use dom::bindings::str::DOMString;
     use dom::node::Node;
     use dom::node::Node;
    use dom::nodelist::NodeList;
    use dom::window::Window;
     use dom_struct::dom_struct;
     use dom_struct::dom_struct;
    use std::default::Default;
      
      
     #[dom_struct]
     #[dom_struct]
Line 114: Line 112:
         //property for target node
         //property for target node
         target: JS<Node>,
         target: JS<Node>,
    }
   
    impl MutationRecord {
        fn new(window: &Window, record_type: DOMString, target: JS<Node>) -> MutationRecord {
            MutationRecord {
                reflector_: Reflector::new(),
                record_type: record_type,
                target: target,
            }
        }
     }
     }
      
      
Line 129: Line 117:
         // https://dom.spec.whatwg.org/#dom-mutationrecord-type
         // https://dom.spec.whatwg.org/#dom-mutationrecord-type
         fn Type(&self) -> DOMString {
         fn Type(&self) -> DOMString {
             return self.record_type;
             self.record_type.clone()
         }
         }
      
      
Line 136: Line 124:
             return Root::from_ref(&*self.target);
             return Root::from_ref(&*self.target);
         }
         }
   
     }
     }


Line 144: Line 131:
     pub mod mutationobserver;
     pub mod mutationobserver;
     pub mod mutationrecord;
     pub mod mutationrecord;
    ...
***Added the new Interface bindings in the file:
    '''components/script/dom/bindings/codegen/Bindings.conf'''
    ...
    DOMInterfaces = {
    ...
    'MutationObserver': {
      'nativeType': 'MutationObserver',
      'path': 'dom::mutationobserver::MutationObserver',
    },
   
    'MutationRecord': {
      'nativeType': 'MutationRecord',
      'path': 'dom::mutationrecord::MutationRecord',
    },
    ...
    }
     ...
     ...
**We hid the new interfaces by default by adding a <code>[Pref="dom.mutation_observer.enabled"]</code> attribute to both <code>mutationobserver.rs</code> and <code>mutationrecord.rs</code> and added a corresponding preference to <code>resources/prefs.json</code> file.
**We hid the new interfaces by default by adding a <code>[Pref="dom.mutation_observer.enabled"]</code> attribute to both <code>mutationobserver.rs</code> and <code>mutationrecord.rs</code> and added a corresponding preference to <code>resources/prefs.json</code> file.
Line 173: Line 143:
       ...
       ...
       /// a vector of MutationObserver objects
       /// a vector of MutationObserver objects
       mutation_observers: Vec<MutationObserver>,
       mutation_observers: DOMRefCell<Vec<JS<MutationObserver>>>,
       ...
       ...
     }
     }
     ...
     ...
     impl ScriptThread {
     impl ScriptThread {
      ...
        ...
      mutation_observers: vec![],
        pub fn add_mutation_observer(observer: &MutationObserver) {
      ...
            SCRIPT_THREAD_ROOT.with(|root| {
                let script_thread = unsafe { &*root.get().unwrap() };
                script_thread.mutation_observers.borrow_mut().push(JS::from_ref(observer));
            })
        }
        ...
     }
     }
     ...
      
*Implemented the <code>MutationObserver</code> constructor, by following the steps given in the [https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver DOM Specifications].
*Implemented the <code>MutationObserver</code> constructor, by following the steps given in the [https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver DOM Specifications].
     '''components/script/dom/mutationobserver.rs'''
     '''components/script/dom/mutationobserver.rs'''
     ...
     ...
     pub fn Constructor(global: &GlobalScope, callback: Rc<MutationCallback>) -> Result<Root<MutationObserver>, Error> {
     pub fn Constructor(global: &Window, callback: Rc<MutationCallback>) -> Fallible<Root<MutationObserver>> {
      callback = &callback
        let observer = MutationObserver::new(global, callback);
      let mut frameBrowsingContext = Frames(&self)
        ScriptThread::add_mutation_observer(&*observer);
     
        Ok(observer)
      Ok(MutationObserver::new(global,this.callback)),
      Err(Error::new())
     }
     }
     ...
     ...
Line 199: Line 172:


* The following are the tests written with regard to attributes of a Mutation Observer ( i.e <code>.tests/wpt/web-platform-tests/dom/nodes/MutationObserver-attributes.html</code>).
* The following are the tests written with regard to attributes of a Mutation Observer ( i.e <code>.tests/wpt/web-platform-tests/dom/nodes/MutationObserver-attributes.html</code>).
** The scenario where there is no old value in the document and an update happens.
** The scenario where there is no old attribute in the document and an update happens on the attribute.
** The scenario where there is a old value and an update happens.
** The scenario where there is a old attribute and an update happens.
** The scenario where the old value is an empty string.
** The scenario where the old attribute is an empty string.
** The scenario where the new update is the same as the old value.
** The scenario where the new update is the same as the old value.
** The scenario where multiple values in a document are updated.
** The scenario where multiple attributes in a document are updated.
** The scenario where there is invalid character in the updated value.
** The scenario where there is invalid character in the updated attribute.
** The scenario where multiple old attributes are removed.
** The scenario where multiple old attributes are removed.
** The scenario where new attribute is added.
** The scenario where new attribute is added.
Line 226: Line 199:


* The following are the tests written with regard to Character data of a Mutation Observer ( i.e <code>.tests/wpt/web-platform-tests/dom/nodes/MutationObserver-characterData.html</code>).
* The following are the tests written with regard to Character data of a Mutation Observer ( i.e <code>.tests/wpt/web-platform-tests/dom/nodes/MutationObserver-characterData.html</code>).
** The scenario where there is a updation without old value.
** The scenario where there is a updation without old data.
** The scenario where there is a updation with an old value.
** The scenario where there is a updation with an old data.
** The scenario where there is a addition to an existing data.
** The scenario where there is a addition to an existing data.
** The scenario where there is a addition to an empty string.
** The scenario where there is a addition to an empty string.
Line 233: Line 206:
** The scenario when a child is removed and data is updated.
** The scenario when a child is removed and data is updated.


 
* The following are the tests written with regard to Records of a Mutation Observer ( i.e <code>.tests/wpt/web-platform-tests/dom/nodes/MutationObserver-takeRecords.html</code>).
** The test to check if attributes, Child list and Character Data are present or not.
 


*Added a <code>__dir__.ini </code> file to <code>tests/wpt/metadata/dom/nodes/</code> for enabling the new preference by including <code>"prefs: ["dom.mutation_observer.enabled:true"]"</code>.
*Added a <code>__dir__.ini </code> file to <code>tests/wpt/metadata/dom/nodes/</code> for enabling the new preference by including <code>"prefs: ["dom.mutation_observer.enabled:true"]"</code>.

Latest revision as of 19:54, 5 April 2017

Mutation Observer API Project with SERVO & RUST

Servo is a prototype web browser engine written in the RUST language. The DOM standard defines a MutationObserver API that allows web content to receive callbacks when the page contents are mutated. In simple words, MutationObserver facilitates the developers with a technique to react to the changes in DOM. MutationObserver is designed to replace the Mutation Events defined in the DOM3 Events specification. The goal of this project is to implement the fundamental pieces required to support the Mutation Observer API.

Introduction

Servo

Servo is an open source web browser engine designed for application and embedded use. It is a high performance browser engine created by Mozilla Research and is written in the Rust language. The main idea behind the Servo is to create a parallel environment where the components are handle by isolated and fine grained tasks. These components can be rendering, HTML parsing etc.

Rust

Rust is an open source systems programming language developed by Mozilla. Servo is written in Rust. The main purpose behind it's design is to be thread safe and concurrent. The emphasis is also on speed, safety and control of memory layout.

Tracking issue

The Original Tracking Issue for the project can be found at the Tracking Issue page of the Mutation Observer Project.

Building the Project

Follow the Guidelines mentioned in the Read me file of the project Wiki Page for building the Project in a Linux Environment.

Project Description

  • The changes made for the project are as per the initial steps given here.

Initial steps for Stub method Implementation:

  • Forked the original (Servo)[1] repository into our GitHub account and created a new Branch (MutationObserver-dev)[2] for pushing our changes.
   MutationObserver IDL
   [Constructor(MutationCallback callback)]
   interface MutationObserver {
     //void observe(Node target, optional MutationObserverInit options);
     //void disconnect();
     //sequence<MutationRecord> takeRecords();
   };
   
   callback MutationCallback  = void (sequence<MutationRecord> mutations, MutationObserver observer);
   
   dictionary MutationObserverInit {
     boolean childList = false;
     boolean attributes;
     boolean characterData;
     boolean subtree = false;
     boolean attributeOldValue;
     boolean characterDataOldValue;
     sequence<DOMString> attributeFilter;
   };
   MutationRecord IDL
   [Exposed=Window]
   interface MutationRecord {
     readonly attribute DOMString type;
     [SameObject] readonly attribute Node target;
     //[SameObject] readonly attribute NodeList addedNodes;
     //[SameObject] readonly attribute NodeList removedNodes;
     //readonly attribute Node? previousSibling;
     //readonly attribute Node? nextSibling;
     //readonly attribute DOMString? attributeName;
     //readonly attribute DOMString? attributeNamespace;
     //readonly attribute DOMString? oldValue;
   };
      • Added the new interfaces in the dom scripts folder.
    • mutationobserver.rs contains the implementation of the MutationObserver IDL interface.
   components/script/dom/mutationobserver.rs
   use dom::bindings::codegen::Bindings::MutationObserverBinding;
   use dom::bindings::codegen::Bindings::MutationObserverBinding::MutationCallback;
   use dom::bindings::error::Fallible;
   use dom::bindings::js::Root;
   use dom::bindings::reflector::{Reflector, reflect_dom_object};
   use dom::window::Window;
   use dom_struct::dom_struct;
   use script_thread::ScriptThread;
   use std::rc::Rc;
   
   #[dom_struct]
   pub struct MutationObserver {
       reflector_: Reflector,
       #[ignore_heap_size_of = "can't measure Rc values"]
       callback: Rc<MutationCallback>,
   }
   
   impl MutationObserver {
       fn new(global: &Window, callback: Rc<MutationCallback>) -> Root<MutationObserver> {
           let boxed_observer = box MutationObserver::new_inherited(callback);
           reflect_dom_object(boxed_observer, global, MutationObserverBinding::Wrap)
       }
       
       fn new_inherited(callback: Rc<MutationCallback>) -> MutationObserver {
           MutationObserver {
               reflector_: Reflector::new(),
               callback: callback,
           }
       }
       
       pub fn Constructor(global: &Window, callback: Rc<MutationCallback>) -> Fallible<Root<MutationObserver>> {
           let observer = MutationObserver::new(global, callback);
           ScriptThread::add_mutation_observer(&*observer);
           Ok(observer)
       }
   }
    • mutationrecord.rs contains the implementation of the MutationRecord IDL interface. It also contains the 'getter' functions for the private methods.
   components/script/dom/mutationrecord.rs
   use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding::MutationRecordMethods;
   use dom::bindings::js::{JS, Root};
   use dom::bindings::reflector::Reflector;
   use dom::bindings::str::DOMString;
   use dom::node::Node;
   use dom_struct::dom_struct;
   
   #[dom_struct]
   pub struct MutationRecord {
       reflector_: Reflector,
   
       //property for record type
       record_type: DOMString,
   
       //property for target node
       target: JS<Node>,
   }
   
   impl MutationRecordMethods for MutationRecord {
       // https://dom.spec.whatwg.org/#dom-mutationrecord-type
       fn Type(&self) -> DOMString {
           self.record_type.clone()
       }
   
       // https://dom.spec.whatwg.org/#dom-mutationrecord-target
       fn Target(&self) -> Root<Node> {
           return Root::from_ref(&*self.target);
       }
   }
      • Added the new interfaces in the mod.rs file:
   components/script/dom/mod.rs
   ...
   pub mod mutationobserver;
   pub mod mutationrecord;
   ...
    • We hid the new interfaces by default by adding a [Pref="dom.mutation_observer.enabled"] attribute to both mutationobserver.rs and mutationrecord.rs and added a corresponding preference to resources/prefs.json file.
   resources/prefs.json
   ...
   "dom.mutation_observer.enabled": false,
   ...
  • We added a vector of MutationObserver objects as a member of the ScriptThread as mentioed in the requirements.
   components/script/script_thread.rs
   ...
   pub struct ScriptThread {
     ...
     /// a vector of MutationObserver objects
     mutation_observers: DOMRefCell<Vec<JS<MutationObserver>>>,
     ...
   }
   ...
   impl ScriptThread {
       ...
       pub fn add_mutation_observer(observer: &MutationObserver) {
           SCRIPT_THREAD_ROOT.with(|root| {
               let script_thread = unsafe { &*root.get().unwrap() };
               script_thread.mutation_observers.borrow_mut().push(JS::from_ref(observer));
           })
       }
       ...
   }
   
  • Implemented the MutationObserver constructor, by following the steps given in the DOM Specifications.
   components/script/dom/mutationobserver.rs
   ...
   pub fn Constructor(global: &Window, callback: Rc<MutationCallback>) -> Fallible<Root<MutationObserver>> {
       let observer = MutationObserver::new(global, callback);
       ScriptThread::add_mutation_observer(&*observer);
       Ok(observer)
   }
   ...

Test Plan

  • Tests are present in web-platform-tests directory (i.e .tests/wpt/web-platform-tests/dom/nodes).
  • The following are the tests written with regard to attributes of a Mutation Observer ( i.e .tests/wpt/web-platform-tests/dom/nodes/MutationObserver-attributes.html).
    • The scenario where there is no old attribute in the document and an update happens on the attribute.
    • The scenario where there is a old attribute and an update happens.
    • The scenario where the old attribute is an empty string.
    • The scenario where the new update is the same as the old value.
    • The scenario where multiple attributes in a document are updated.
    • The scenario where there is invalid character in the updated attribute.
    • The scenario where multiple old attributes are removed.
    • The scenario where new attribute is added.
    • The scenario where there is no Childlist in the mutation.


  • The following are the tests written with regard to Childlist of a Mutation Observer ( i.e .tests/wpt/web-platform-tests/dom/nodes/MutationObserver-childList.html).
    • The scenario where there is no mutation in the Childlist node.
    • The scenario where there is updation on the contents of Childlist node.
    • The scenario where there is updation on the contents of Childlist node on a previously empty content.
    • The scenario where there is removal of contents in the Childlist node.
    • The scenario where there is both addition and removal of contents in the Childlist node.
    • The scenario where there is addition of contents into the last Child.
    • The scenario where we append a child to Childlist node.
    • The scenario where a child is removed.
    • The scenario where both child and data is removed.
    • The scenario where a child is inserted.
    • The scenario where children are inserted.
    • The scenario where children are both removed and added.


  • The following are the tests written with regard to Character data of a Mutation Observer ( i.e .tests/wpt/web-platform-tests/dom/nodes/MutationObserver-characterData.html).
    • The scenario where there is a updation without old data.
    • The scenario where there is a updation with an old data.
    • The scenario where there is a addition to an existing data.
    • The scenario where there is a addition to an empty string.
    • The scenario where there is a removal of data and an empty string is created.
    • The scenario when a child is removed and data is updated.


  • The following are the tests written with regard to Records of a Mutation Observer ( i.e .tests/wpt/web-platform-tests/dom/nodes/MutationObserver-takeRecords.html).
    • The test to check if attributes, Child list and Character Data are present or not.


  • Added a __dir__.ini file to tests/wpt/metadata/dom/nodes/ for enabling the new preference by including "prefs: ["dom.mutation_observer.enabled:true"]".
   tests/wpt/metadata/dom/nodes/__dir__.ini
   prefs: ["dom.mutation_observer.enabled:true"]


  • The web-platform tests can be run by giving the following command at the root directory of the project:
   ./mach test-wpt tests/wpt/web-platform-tests/dom/nodes.
  • We have Updated the corresponding test expectations as per the given guidelines in the tests/wpt/metadata/dom/nodes/ directory.

Design Patterns

Our Project follows the Observer Pattern, where a list of the objects dependent on the current object is maintained and the dependents are notified of any state changes. The Mutation Observer helps in achieving this functionality.

Conclusion

After making the above mentioned changes, we can create the Mutation Observer Object using the Constructor which we have implemented.


Subsequent Steps

After completing the above initial steps, we plan on performing the following tasks:

Commit Frequency

As per the guidelines of the Mozilla project, all the changes will be submitted in a single commit.

References

1. https://doc.rust-lang.org/stable/book/
2. https://en.wikipedia.org/wiki/Rust_(programming_language)
3. https://en.wikipedia.org/wiki/Servo_(layout_engine)
4. https://github.com/servo/servo
5. https://github.com/servo/servo/wiki/Mutation-observer-project
6. https://dom.spec.whatwg.org/