build/mobile/robocop/FennecNativeDriver.java

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko;
     7 import java.io.BufferedOutputStream;
     8 import java.io.BufferedReader;
     9 import java.io.DataOutputStream;
    10 import java.io.File;
    11 import java.io.FileOutputStream;
    12 import java.io.FileReader;
    13 import java.io.FileWriter;
    14 import java.io.IOException;
    15 import java.io.PrintWriter;
    16 import java.nio.IntBuffer;
    17 import java.util.HashMap;
    18 import java.util.List;
    19 import java.util.Map;
    21 import org.json.JSONException;
    22 import org.json.JSONObject;
    23 import org.mozilla.gecko.gfx.LayerView;
    24 import org.mozilla.gecko.gfx.PanningPerfAPI;
    25 import org.mozilla.gecko.util.GeckoEventListener;
    27 import android.app.Activity;
    28 import android.util.Log;
    29 import android.view.View;
    31 import com.jayway.android.robotium.solo.Solo;
    33 public class FennecNativeDriver implements Driver {
    34     private static final int FRAME_TIME_THRESHOLD = 25;     // allow 25ms per frame (40fps)
    36     private Activity mActivity;
    37     private Solo mSolo;
    38     private String mRootPath;
    40     private static String mLogFile = null;
    41     private static LogLevel mLogLevel = LogLevel.INFO;
    43     public enum LogLevel {
    44         DEBUG(1),
    45         INFO(2),
    46         WARN(3),
    47         ERROR(4);
    49         private int mValue;
    50         LogLevel(int value) {
    51             mValue = value;
    52         }
    53         public boolean isEnabled(LogLevel configuredLevel) {
    54             return mValue >= configuredLevel.getValue();
    55         }
    56         private int getValue() {
    57             return mValue;
    58         }
    59     }
    61     public FennecNativeDriver(Activity activity, Solo robocop, String rootPath) {
    62         mActivity = activity;
    63         mSolo = robocop;
    64         mRootPath = rootPath;
    65     }
    67     //Information on the location of the Gecko Frame.
    68     private boolean mGeckoInfo = false;
    69     private int mGeckoTop = 100;
    70     private int mGeckoLeft = 0;
    71     private int mGeckoHeight= 700;
    72     private int mGeckoWidth = 1024;
    74     private void getGeckoInfo() {
    75         View geckoLayout = mActivity.findViewById(R.id.gecko_layout);
    76         if (geckoLayout != null) {
    77             int[] pos = new int[2];
    78             geckoLayout.getLocationOnScreen(pos);
    79             mGeckoTop = pos[1];
    80             mGeckoLeft = pos[0];
    81             mGeckoWidth = geckoLayout.getWidth();
    82             mGeckoHeight = geckoLayout.getHeight();
    83             mGeckoInfo = true;
    84         } else {
    85             throw new RoboCopException("Unable to find view gecko_layout");
    86         }
    87     }
    89     public int getGeckoTop() {
    90         if (!mGeckoInfo) {
    91             getGeckoInfo();
    92         }
    93         return mGeckoTop;
    94     }
    96     public int getGeckoLeft() {
    97         if (!mGeckoInfo) {
    98             getGeckoInfo();
    99         }
   100         return mGeckoLeft;
   101     }
   103     public int getGeckoHeight() {
   104         if (!mGeckoInfo) {
   105             getGeckoInfo();
   106         }
   107         return mGeckoHeight;
   108     }
   110     public int getGeckoWidth() {
   111         if (!mGeckoInfo) {
   112             getGeckoInfo();
   113         }
   114         return mGeckoWidth;
   115     }
   117     /** Find the element with given id.
   118      *
   119      *  @return An Element representing the view, or null if the view is not found.
   120      */
   121     public Element findElement(Activity activity, int id) {
   122         return new FennecNativeElement(id, activity);
   123     }
   125     public void startFrameRecording() {
   126         PanningPerfAPI.startFrameTimeRecording();
   127     }
   129     public int stopFrameRecording() {
   130         final List<Long> frames = PanningPerfAPI.stopFrameTimeRecording();
   131         int badness = 0;
   132         for (int i = 1; i < frames.size(); i++) {
   133             long frameTime = frames.get(i) - frames.get(i - 1);
   134             int delay = (int)(frameTime - FRAME_TIME_THRESHOLD);
   135             // for each frame we miss, add the square of the delay. This
   136             // makes large delays much worse than small delays.
   137             if (delay > 0) {
   138                 badness += delay * delay;
   139             }
   140         }
   142         // Don't do any averaging of the numbers because really we want to
   143         // know how bad the jank was at its worst
   144         return badness;
   145     }
   147     public void startCheckerboardRecording() {
   148         PanningPerfAPI.startCheckerboardRecording();
   149     }
   151     public float stopCheckerboardRecording() {
   152         final List<Float> checkerboard = PanningPerfAPI.stopCheckerboardRecording();
   153         float total = 0;
   154         for (float val : checkerboard) {
   155             total += val;
   156         }
   157         return total * 100.0f;
   158     }
   160     private LayerView getSurfaceView() {
   161         final LayerView layerView = mSolo.getView(LayerView.class, 0);
   163         if (layerView == null) {
   164             log(LogLevel.WARN, "getSurfaceView could not find LayerView");
   165             for (final View v : mSolo.getViews()) {
   166                 log(LogLevel.WARN, "  View: " + v);
   167             }
   168         }
   169         return layerView;
   170     }
   172     public PaintedSurface getPaintedSurface() {
   173         final LayerView view = getSurfaceView();
   174         if (view == null) {
   175             return null;
   176         }
   178         final IntBuffer pixelBuffer = view.getPixels();
   180         // now we need to (1) flip the image, because GL likes to do things up-side-down,
   181         // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
   182         int w = view.getWidth();
   183         int h = view.getHeight();
   184         pixelBuffer.position(0);
   185         String mapFile = mRootPath + "/pixels.map";
   187         FileOutputStream fos = null;
   188         BufferedOutputStream bos = null;
   189         DataOutputStream dos = null;
   190         try {
   191             fos = new FileOutputStream(mapFile);
   192             bos = new BufferedOutputStream(fos);
   193             dos = new DataOutputStream(bos);
   195             for (int y = h - 1; y >= 0; y--) {
   196                 for (int x = 0; x < w; x++) {
   197                     int agbr = pixelBuffer.get();
   198                     dos.writeInt((agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000));
   199                 }
   200             }
   201         } catch (IOException e) {
   202             throw new RoboCopException("exception with pixel writer on file: " + mapFile);
   203         } finally {
   204             try {
   205                 if (dos != null) {
   206                     dos.flush();
   207                     dos.close();
   208                 }
   209                 // closing dos automatically closes bos
   210                 if (fos != null) {
   211                     fos.flush();
   212                     fos.close();
   213                 }
   214             } catch (IOException e) {
   215                 log(LogLevel.ERROR, e);
   216                 throw new RoboCopException("exception closing pixel writer on file: " + mapFile);
   217             }
   218         }
   219         return new PaintedSurface(mapFile, w, h);
   220     }
   222     public int mHeight=0;
   223     public int mScrollHeight=0;
   224     public int mPageHeight=10;
   226     public int getScrollHeight() {
   227         return mScrollHeight;
   228     }
   229     public int getPageHeight() {
   230         return mPageHeight;
   231     }
   232     public int getHeight() {
   233         return mHeight;
   234     }
   236     public void setupScrollHandling() {
   237         GeckoAppShell.registerEventListener("robocop:scroll", new GeckoEventListener() {
   238             @Override
   239             public void handleMessage(final String event, final JSONObject message) {
   240                 try {
   241                     mScrollHeight = message.getInt("y");
   242                     mHeight = message.getInt("cheight");
   243                     // We don't want a height of 0. That means it's a bad response.
   244                     if (mHeight > 0) {
   245                         mPageHeight = message.getInt("height");
   246                     }
   247                 } catch (JSONException e) {
   248                     FennecNativeDriver.log(FennecNativeDriver.LogLevel.WARN,
   249                             "WARNING: ScrollReceived, but message does not contain " +
   250                             "expected fields: " + e);
   251                 }
   252             }
   253         });
   254     }
   256     /**
   257      *  Takes a filename, loads the file, and returns a string version of the entire file.
   258      */
   259     public static String getFile(String filename)
   260     {
   261         StringBuilder text = new StringBuilder();
   263         BufferedReader br = null;
   264         try {
   265             br = new BufferedReader(new FileReader(filename));
   266             String line;
   268             while ((line = br.readLine()) != null) {
   269                 text.append(line);
   270                 text.append('\n');
   271             }
   272         } catch (IOException e) {
   273             log(LogLevel.ERROR, e);
   274         } finally {
   275             try {
   276                 br.close();
   277             } catch (IOException e) {
   278             }
   279         }
   280         return text.toString();    
   281     }
   283     /**
   284      *  Takes a string of "key=value" pairs split by \n and creates a hash table.
   285      */
   286     public static Map<String, String> convertTextToTable(String data)
   287     {
   288         HashMap<String, String> retVal = new HashMap<String, String>();
   290         String[] lines = data.split("\n");
   291         for (int i = 0; i < lines.length; i++) {
   292             String[] parts = lines[i].split("=", 2);
   293             retVal.put(parts[0].trim(), parts[1].trim());
   294         }
   295         return retVal;
   296     }
   298     public static void logAllStackTraces(LogLevel level) {
   299         StringBuffer sb = new StringBuffer();
   300         sb.append("Dumping ALL the threads!\n");
   301         Map<Thread, StackTraceElement[]> allStacks = Thread.getAllStackTraces();
   302         for (Thread t : allStacks.keySet()) {
   303             sb.append(t.toString()).append('\n');
   304             for (StackTraceElement ste : allStacks.get(t)) {
   305                 sb.append(ste.toString()).append('\n');
   306             }
   307             sb.append('\n');
   308         }
   309         log(level, sb.toString());
   310     }
   312     /** 
   313      *  Set the filename used for logging. If the file already exists, delete it
   314      *  as a safe-guard against accidentally appending to an old log file.
   315      */
   316     public static void setLogFile(String filename) {
   317         mLogFile = filename;
   318         File file = new File(mLogFile);
   319         if (file.exists()) {
   320             file.delete();
   321         }
   322     }
   324     public static void setLogLevel(LogLevel level) {
   325         mLogLevel = level;
   326     }
   328     public static void log(LogLevel level, String message) {
   329         log(level, message, null);
   330     }
   332     public static void log(LogLevel level, Throwable t) {
   333         log(level, null, t);
   334     }
   336     public static void log(LogLevel level, String message, Throwable t) {
   337         if (mLogFile == null) {
   338             assert(false);
   339         }
   341         if (level.isEnabled(mLogLevel)) {
   342             PrintWriter pw = null;
   343             try {
   344                 pw = new PrintWriter(new FileWriter(mLogFile, true));
   345                 if (message != null) {
   346                     pw.println(message);
   347                 }
   348                 if (t != null) {
   349                     t.printStackTrace(pw);
   350                 }
   351             } catch (IOException ioe) {
   352                 Log.e("Robocop", "exception with file writer on: " + mLogFile);
   353             } finally {
   354                 pw.close();
   355             }
   356             // PrintWriter doesn't throw IOE but sets an error flag instead,
   357             // so check for that
   358             if (pw.checkError()) {
   359                 Log.e("Robocop", "exception with file writer on: " + mLogFile);
   360             }
   361         }
   363         if (level == LogLevel.INFO) {
   364             Log.i("Robocop", message, t);
   365         } else if (level == LogLevel.DEBUG) {
   366             Log.d("Robocop", message, t);
   367         } else if (level == LogLevel.WARN) {
   368             Log.w("Robocop", message, t);
   369         } else if (level == LogLevel.ERROR) {
   370             Log.e("Robocop", message, t);
   371         }
   372     }
   373 }

mercurial