 /*******************************************************************************************************************************
 * It's Browser Time!
 * 
 *
 * Copyright (C) 2013 by Tobias Lidskog (https://twitter.com/tobiaslidskog) &  Peter Hedenskog (http://peterhedenskog.com)
 *
 ********************************************************************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 
 * compliance with the License. You may obtain a copy of the License at
 * 
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is 
 * distributed  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   
 * See the License for the specific language governing permissions and limitations under the License.
 *
 ********************************************************************************************************************************
 */
package com.soulgalore.web.browsertime.datacollector;

import com.soulgalore.web.browsertime.timings.TimingMark;
import com.soulgalore.web.browsertime.timings.TimingMeasurement;
import com.soulgalore.web.browsertime.timings.TimingRun;

import org.openqa.selenium.JavascriptExecutor;

import java.util.List;
import java.util.Map;

/**
 * Marks and measurements defined in the w3c user timing recommendation.
 * http://www.w3.org/TR/user-timing/
 *
 * NOTE: The user timing spec uses a different resolution for time stamps (milliseconds with a decimal part)
 */
public class UserTimingDataCollector extends TimingDataCollector {
    private static final String LIST_PAGE_DEFINED_MARKS =
            "return window.performance.getEntriesByType('mark');";

    private static final String LIST_PAGE_DEFINED_MEASUREMENTS =
            "return window.performance.getEntriesByType('measure');";

    private boolean shouldAddMeasurementsForUserMarks;

    public UserTimingDataCollector(boolean shouldAddMeasurementsForUserMarks) {
        this.shouldAddMeasurementsForUserMarks = shouldAddMeasurementsForUserMarks;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void collectMarks(JavascriptExecutor js, TimingRun results) {
        if (!isPageDefinedTimingsSupported(js)) {
            return;
        }

        List marks = (List) js.executeScript(LIST_PAGE_DEFINED_MARKS);

        double referenceTime = getNavigationStart(results);

        for (Object m : marks) {
            Map mark = (Map) m;
            String name = (String) mark.get("name");
            double startTime = (Double) mark.get("startTime") + referenceTime;
            results.addMark(new TimingMark(name, startTime));
        }
    }

    @Override
    public void collectMeasurements(JavascriptExecutor js, TimingRun results) {
        if (!isPageDefinedTimingsSupported(js)) {
            return;
        }

        double referenceTime = getNavigationStart(results);

        if (shouldAddMeasurementsForUserMarks) {
            // create synthetic measurements for each mark, in order to easily get both start time and duration.
            List marks = (List) js.executeScript(LIST_PAGE_DEFINED_MARKS);

            for (Object m : marks) {
                Map mark = (Map) m;
                String name = (String) mark.get("name");
                double duration = (Double) mark.get("startTime");
                results.addMeasurement(new TimingMeasurement(name, referenceTime, duration));
            }
        }

        List measurements = (List) js.executeScript(LIST_PAGE_DEFINED_MEASUREMENTS);

        for (Object m : measurements) {
            Map measurement = (Map) m;
            String name = (String) measurement.get("name");
            double startTime = (Double) measurement.get("startTime") + referenceTime;
            double duration = (Double) measurement.get("duration");
            results.addMeasurement(new TimingMeasurement(name, startTime, duration));
        }

    }

    private boolean isPageDefinedTimingsSupported(JavascriptExecutor js) {
        return (Boolean) js
                .executeScript("return !!(window.performance && window.performance.getEntriesByType);");
    }

    private double getNavigationStart(TimingRun results) {
        TimingMark start = results.getMark("navigationStart");

        return start != null ? start.getStartTime() : 0;
    }
}

@@ -2487,32 +2487,65 @@
 EFINED_MARKS);%0A%0A
+        if (marks != null) %7B%0A    
         double r
@@ -2589,32 +2589,36 @@
 ults);%0A%0A        
+ 
+   
 for (Object m : 
@@ -2630,32 +2630,36 @@
 ) %7B%0A            
+    
 Map mark = (Map)
@@ -2666,32 +2666,36 @@
  m;%0A            
+  
+  
 String name = (S
@@ -2711,32 +2711,36 @@
 rk.get(%22name%22);%0A
+    
             doub
@@ -2806,32 +2806,36 @@
 me;%0A            
+  
+  
 results.addMark(
@@ -2864,24 +2864,38 @@
 tartTime));%0A
+            %7D%0A
         %7D%0A  
@@ -3365,32 +3365,69 @@
 EFINED_MARKS);%0A%0A
+            if (marks != null) %7B%0A    
             for 
@@ -3455,32 +3455,36 @@
                 
+    
 Map mark = (Map)
@@ -3479,32 +3479,36 @@
 mark = (Map) m;%0A
+    
                 
@@ -3556,32 +3556,36 @@
                 
+ 
+   
 double duration 
@@ -3614,24 +3614,28 @@
 tartTime%22);%0A
+    
             
@@ -3708,32 +3708,50 @@
 me, duration));%0A
+                %7D%0A
             %7D%0A  
@@ -3841,24 +3841,64 @@
 UREMENTS);%0A%0A
+        if (measurements != null) %7B%0A    
         for 
@@ -3933,24 +3933,28 @@
             
+    
 Map measurem
@@ -3972,32 +3972,36 @@
  m;%0A            
+   
+ 
 String name = (S
@@ -4024,32 +4024,36 @@
 nt.get(%22name%22);%0A
+    
             doub
@@ -4114,32 +4114,36 @@
  referenceTime;%0A
+    
             doub
@@ -4198,32 +4198,36 @@
 %22);%0A            
+    
 results.addMeasu
@@ -4276,32 +4276,46 @@
 me, duration));%0A
+            %7D%0A
         %7D%0A%0A    %7D

Add null checks for mark list and measure list returned by browser.