mobile/android/base/Tab.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 package org.mozilla.gecko;
     8 import java.util.ArrayList;
     9 import java.util.Collection;
    10 import java.util.HashMap;
    11 import java.util.regex.Matcher;
    12 import java.util.regex.Pattern;
    14 import org.json.JSONException;
    15 import org.json.JSONObject;
    16 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
    17 import org.mozilla.gecko.db.BrowserDB;
    18 import org.mozilla.gecko.gfx.Layer;
    19 import org.mozilla.gecko.util.ThreadUtils;
    21 import android.content.ContentResolver;
    22 import android.content.Context;
    23 import android.graphics.Bitmap;
    24 import android.graphics.Color;
    25 import android.graphics.drawable.BitmapDrawable;
    26 import android.os.Build;
    27 import android.text.TextUtils;
    28 import android.util.Log;
    29 import android.view.View;
    31 public class Tab {
    32     private static final String LOGTAG = "GeckoTab";
    34     private static Pattern sColorPattern;
    35     private final int mId;
    36     private long mLastUsed;
    37     private String mUrl;
    38     private String mBaseDomain;
    39     private String mUserSearch;
    40     private String mTitle;
    41     private Bitmap mFavicon;
    42     private String mFaviconUrl;
    43     private int mFaviconSize;
    44     private boolean mHasFeeds;
    45     private boolean mHasOpenSearch;
    46     private SiteIdentity mSiteIdentity;
    47     private boolean mReaderEnabled;
    48     private BitmapDrawable mThumbnail;
    49     private int mHistoryIndex;
    50     private int mHistorySize;
    51     private int mParentId;
    52     private boolean mExternal;
    53     private boolean mBookmark;
    54     private boolean mReadingListItem;
    55     private int mFaviconLoadId;
    56     private String mContentType;
    57     private boolean mHasTouchListeners;
    58     private ZoomConstraints mZoomConstraints;
    59     private boolean mIsRTL;
    60     private ArrayList<View> mPluginViews;
    61     private HashMap<Object, Layer> mPluginLayers;
    62     private int mBackgroundColor;
    63     private int mState;
    64     private Bitmap mThumbnailBitmap;
    65     private boolean mDesktopMode;
    66     private boolean mEnteringReaderMode;
    67     private Context mAppContext;
    68     private ErrorType mErrorType = ErrorType.NONE;
    69     private static final int MAX_HISTORY_LIST_SIZE = 50;
    70     private volatile int mLoadProgress;
    71     private volatile int mRecordingCount = 0;
    72     private String mMostRecentHomePanel;
    74     public static final int STATE_DELAYED = 0;
    75     public static final int STATE_LOADING = 1;
    76     public static final int STATE_SUCCESS = 2;
    77     public static final int STATE_ERROR = 3;
    79     public static final int LOAD_PROGRESS_INIT = 10;
    80     public static final int LOAD_PROGRESS_START = 20;
    81     public static final int LOAD_PROGRESS_LOCATION_CHANGE = 60;
    82     public static final int LOAD_PROGRESS_LOADED = 80;
    83     public static final int LOAD_PROGRESS_STOP = 100;
    85     private static final int DEFAULT_BACKGROUND_COLOR = Color.WHITE;
    87     public enum ErrorType {
    88         CERT_ERROR,  // Pages with certificate problems
    89         BLOCKED,     // Pages blocked for phishing or malware warnings
    90         NET_ERROR,   // All other types of error
    91         NONE         // Non error pages
    92     }
    94     public Tab(Context context, int id, String url, boolean external, int parentId, String title) {
    95         mAppContext = context.getApplicationContext();
    96         mId = id;
    97         mLastUsed = 0;
    98         mUrl = url;
    99         mBaseDomain = "";
   100         mUserSearch = "";
   101         mExternal = external;
   102         mParentId = parentId;
   103         mTitle = title == null ? "" : title;
   104         mFavicon = null;
   105         mFaviconUrl = null;
   106         mFaviconSize = 0;
   107         mHasFeeds = false;
   108         mHasOpenSearch = false;
   109         mSiteIdentity = new SiteIdentity();
   110         mReaderEnabled = false;
   111         mEnteringReaderMode = false;
   112         mThumbnail = null;
   113         mHistoryIndex = -1;
   114         mHistorySize = 0;
   115         mBookmark = false;
   116         mReadingListItem = false;
   117         mFaviconLoadId = 0;
   118         mContentType = "";
   119         mZoomConstraints = new ZoomConstraints(false);
   120         mPluginViews = new ArrayList<View>();
   121         mPluginLayers = new HashMap<Object, Layer>();
   122         mState = shouldShowProgress(url) ? STATE_LOADING : STATE_SUCCESS;
   123         mLoadProgress = LOAD_PROGRESS_INIT;
   125         // At startup, the background is set to a color specified by LayerView
   126         // when the LayerView is created. Shortly after, this background color
   127         // will be used before the tab's content is shown.
   128         mBackgroundColor = DEFAULT_BACKGROUND_COLOR;
   130         updateBookmark();
   131     }
   133     private ContentResolver getContentResolver() {
   134         return mAppContext.getContentResolver();
   135     }
   137     public void onDestroy() {
   138         Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.CLOSED);
   139     }
   141     public int getId() {
   142         return mId;
   143     }
   145     public synchronized void onChange() {
   146         mLastUsed = System.currentTimeMillis();
   147     }
   149     public synchronized long getLastUsed() {
   150         return mLastUsed;
   151     }
   153     public int getParentId() {
   154         return mParentId;
   155     }
   157     // may be null if user-entered query hasn't yet been resolved to a URI
   158     public synchronized String getURL() {
   159         return mUrl;
   160     }
   162     // mUserSearch should never be null, but it may be an empty string
   163     public synchronized String getUserSearch() {
   164         return mUserSearch;
   165     }
   167     // mTitle should never be null, but it may be an empty string
   168     public synchronized String getTitle() {
   169         return mTitle;
   170     }
   172     public String getDisplayTitle() {
   173         if (mTitle != null && mTitle.length() > 0) {
   174             return mTitle;
   175         }
   177         return mUrl;
   178     }
   180     public String getBaseDomain() {
   181         return mBaseDomain;
   182     }
   184     public Bitmap getFavicon() {
   185         return mFavicon;
   186     }
   188     public BitmapDrawable getThumbnail() {
   189         return mThumbnail;
   190     }
   192     public String getMostRecentHomePanel() {
   193         return mMostRecentHomePanel;
   194     }
   196     public void setMostRecentHomePanel(String panelId) {
   197         mMostRecentHomePanel = panelId;
   198     }
   200     public Bitmap getThumbnailBitmap(int width, int height) {
   201         if (mThumbnailBitmap != null) {
   202             // Bug 787318 - Honeycomb has a bug with bitmap caching, we can't
   203             // reuse the bitmap there.
   204             boolean honeycomb = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
   205                               && Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2);
   206             boolean sizeChange = mThumbnailBitmap.getWidth() != width
   207                               || mThumbnailBitmap.getHeight() != height;
   208             if (honeycomb || sizeChange) {
   209                 mThumbnailBitmap = null;
   210             }
   211         }
   213         if (mThumbnailBitmap == null) {
   214             Bitmap.Config config = (GeckoAppShell.getScreenDepth() == 24) ?
   215                 Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
   216             mThumbnailBitmap = Bitmap.createBitmap(width, height, config);
   217         }
   219         return mThumbnailBitmap;
   220     }
   222     public void updateThumbnail(final Bitmap b) {
   223         ThreadUtils.postToBackgroundThread(new Runnable() {
   224             @Override
   225             public void run() {
   226                 if (b != null) {
   227                     try {
   228                         mThumbnail = new BitmapDrawable(mAppContext.getResources(), b);
   229                         if (mState == Tab.STATE_SUCCESS)
   230                             saveThumbnailToDB();
   231                     } catch (OutOfMemoryError oom) {
   232                         Log.w(LOGTAG, "Unable to create/scale bitmap.", oom);
   233                         mThumbnail = null;
   234                     }
   235                 } else {
   236                     mThumbnail = null;
   237                 }
   239                 Tabs.getInstance().notifyListeners(Tab.this, Tabs.TabEvents.THUMBNAIL);
   240             }
   241         });
   242     }
   244     public synchronized String getFaviconURL() {
   245         return mFaviconUrl;
   246     }
   248     public boolean hasFeeds() {
   249         return mHasFeeds;
   250     }
   252     public boolean hasOpenSearch() {
   253         return mHasOpenSearch;
   254     }
   256     public SiteIdentity getSiteIdentity() {
   257         return mSiteIdentity;
   258     }
   260     public boolean getReaderEnabled() {
   261         return mReaderEnabled;
   262     }
   264     public boolean isBookmark() {
   265         return mBookmark;
   266     }
   268     public boolean isReadingListItem() {
   269         return mReadingListItem;
   270     }
   272     public boolean isExternal() {
   273         return mExternal;
   274     }
   276     public synchronized void updateURL(String url) {
   277         if (url != null && url.length() > 0) {
   278             mUrl = url;
   279         }
   280     }
   282     private synchronized void updateUserSearch(String userSearch) {
   283         mUserSearch = userSearch;
   284     }
   286     public void setErrorType(String type) {
   287         if ("blocked".equals(type))
   288             setErrorType(ErrorType.BLOCKED);
   289         else if ("certerror".equals(type))
   290             setErrorType(ErrorType.CERT_ERROR);
   291         else if ("neterror".equals(type))
   292             setErrorType(ErrorType.NET_ERROR);
   293         else
   294             setErrorType(ErrorType.NONE);
   295     }
   297     public void setErrorType(ErrorType type) {
   298         mErrorType = type;
   299     }
   301     public ErrorType getErrorType() {
   302         return mErrorType;
   303     }
   305     public void setContentType(String contentType) {
   306         mContentType = (contentType == null) ? "" : contentType;
   307     }
   309     public String getContentType() {
   310         return mContentType;
   311     }
   313     public synchronized void updateTitle(String title) {
   314         // Keep the title unchanged while entering reader mode.
   315         if (mEnteringReaderMode) {
   316             return;
   317         }
   319         // If there was a title, but it hasn't changed, do nothing.
   320         if (mTitle != null &&
   321             TextUtils.equals(mTitle, title)) {
   322             return;
   323         }
   325         mTitle = (title == null ? "" : title);
   326         Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.TITLE);
   327     }
   329     public void setState(int state) {
   330         mState = state;
   332         if (mState != Tab.STATE_LOADING)
   333             mEnteringReaderMode = false;
   334     }
   336     public int getState() {
   337         return mState;
   338     }
   340     public void setZoomConstraints(ZoomConstraints constraints) {
   341         mZoomConstraints = constraints;
   342     }
   344     public ZoomConstraints getZoomConstraints() {
   345         return mZoomConstraints;
   346     }
   348     public void setIsRTL(boolean aIsRTL) {
   349         mIsRTL = aIsRTL;
   350     }
   352     public boolean getIsRTL() {
   353         return mIsRTL;
   354     }
   356     public void setHasTouchListeners(boolean aValue) {
   357         mHasTouchListeners = aValue;
   358     }
   360     public boolean getHasTouchListeners() {
   361         return mHasTouchListeners;
   362     }
   364     public void setFaviconLoadId(int faviconLoadId) {
   365         mFaviconLoadId = faviconLoadId;
   366     }
   368     public int getFaviconLoadId() {
   369         return mFaviconLoadId;
   370     }
   372     /**
   373      * Returns true if the favicon changed.
   374      */
   375     public boolean updateFavicon(Bitmap favicon) {
   376         if (mFavicon == favicon) {
   377             return false;
   378         }
   379         mFavicon = favicon;
   380         return true;
   381     }
   383     public synchronized void updateFaviconURL(String faviconUrl, int size) {
   384         // If we already have an "any" sized icon, don't update the icon.
   385         if (mFaviconSize == -1)
   386             return;
   388         // Only update the favicon if it's bigger than the current favicon.
   389         // We use -1 to represent icons with sizes="any".
   390         if (size == -1 || size >= mFaviconSize) {
   391             mFaviconUrl = faviconUrl;
   392             mFaviconSize = size;
   393         }
   394     }
   396     public synchronized void clearFavicon() {
   397         // Keep the favicon unchanged while entering reader mode
   398         if (mEnteringReaderMode)
   399             return;
   401         mFavicon = null;
   402         mFaviconUrl = null;
   403         mFaviconSize = 0;
   404     }
   406     public void setHasFeeds(boolean hasFeeds) {
   407         mHasFeeds = hasFeeds;
   408     }
   410     public void setHasOpenSearch(boolean hasOpenSearch) {
   411         mHasOpenSearch = hasOpenSearch;
   412     }
   414     public void updateIdentityData(JSONObject identityData) {
   415         mSiteIdentity.update(identityData);
   416     }
   418     public void setReaderEnabled(boolean readerEnabled) {
   419         mReaderEnabled = readerEnabled;
   420         Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.MENU_UPDATED);
   421     }
   423     void updateBookmark() {
   424         if (getURL() == null) {
   425             return;
   426         }
   428         ThreadUtils.postToBackgroundThread(new Runnable() {
   429             @Override
   430             public void run() {
   431                 final String url = getURL();
   432                 if (url == null) {
   433                     return;
   434                 }
   436                 final int flags = BrowserDB.getItemFlags(getContentResolver(), url);
   437                 mBookmark = (flags & Bookmarks.FLAG_BOOKMARK) > 0;
   438                 mReadingListItem = (flags & Bookmarks.FLAG_READING) > 0;
   439                 Tabs.getInstance().notifyListeners(Tab.this, Tabs.TabEvents.MENU_UPDATED);
   440             }
   441         });
   442     }
   444     public void addBookmark() {
   445         ThreadUtils.postToBackgroundThread(new Runnable() {
   446             @Override
   447             public void run() {
   448                 String url = getURL();
   449                 if (url == null)
   450                     return;
   452                 BrowserDB.addBookmark(getContentResolver(), mTitle, url);
   453             }
   454         });
   455     }
   457     public void removeBookmark() {
   458         ThreadUtils.postToBackgroundThread(new Runnable() {
   459             @Override
   460             public void run() {
   461                 String url = getURL();
   462                 if (url == null)
   463                     return;
   465                 BrowserDB.removeBookmarksWithURL(getContentResolver(), url);
   466             }
   467         });
   468     }
   470     public void addToReadingList() {
   471         if (!mReaderEnabled)
   472             return;
   474         JSONObject json = new JSONObject();
   475         try {
   476             json.put("tabID", String.valueOf(getId()));
   477         } catch (JSONException e) {
   478             Log.e(LOGTAG, "JSON error - failing to add to reading list", e);
   479             return;
   480         }
   482         GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Add", json.toString());
   483         GeckoAppShell.sendEventToGecko(e);
   484     }
   486     public void toggleReaderMode() {
   487         if (AboutPages.isAboutReader(mUrl)) {
   488             Tabs.getInstance().loadUrl(ReaderModeUtils.getUrlFromAboutReader(mUrl));
   489         } else if (mReaderEnabled) {
   490             mEnteringReaderMode = true;
   491             Tabs.getInstance().loadUrl(ReaderModeUtils.getAboutReaderForUrl(mUrl, mId));
   492         }
   493     }
   495     public boolean isEnteringReaderMode() {
   496         return mEnteringReaderMode;
   497     }
   499     public void doReload() {
   500         GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Reload", "");
   501         GeckoAppShell.sendEventToGecko(e);
   502     }
   504     // Our version of nsSHistory::GetCanGoBack
   505     public boolean canDoBack() {
   506         return mHistoryIndex > 0;
   507     }
   509     public boolean doBack() {
   510         if (!canDoBack())
   511             return false;
   513         GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Back", "");
   514         GeckoAppShell.sendEventToGecko(e);
   515         return true;
   516     }
   518     public boolean showBackHistory() {
   519         if (!canDoBack())
   520             return false;
   521         return this.showHistory(Math.max(mHistoryIndex - MAX_HISTORY_LIST_SIZE, 0), mHistoryIndex, mHistoryIndex);
   522     }
   524     public boolean showForwardHistory() {
   525         if (!canDoForward())
   526             return false;
   527         return this.showHistory(mHistoryIndex, Math.min(mHistorySize - 1, mHistoryIndex + MAX_HISTORY_LIST_SIZE), mHistoryIndex);
   528     }
   530     public boolean showAllHistory() {
   531         if (!canDoForward() && !canDoBack())
   532             return false;
   534         int min = mHistoryIndex - MAX_HISTORY_LIST_SIZE / 2;
   535         int max = mHistoryIndex + MAX_HISTORY_LIST_SIZE / 2;
   536         if (min < 0) {
   537             max -= min;
   538         }
   539         if (max > mHistorySize - 1) {
   540             min -= max - (mHistorySize - 1);
   541             max = mHistorySize - 1;
   542         }
   543         min = Math.max(min, 0);
   545         return this.showHistory(min, max, mHistoryIndex);
   546     }
   548     /**
   549      * This method will show the history starting on fromIndex until toIndex of the history.
   550      */
   551     public boolean showHistory(int fromIndex, int toIndex, int selIndex) {
   552         JSONObject json = new JSONObject();
   553         try {
   554             json.put("fromIndex", fromIndex);
   555             json.put("toIndex", toIndex);
   556             json.put("selIndex", selIndex);
   557         } catch (JSONException e) {
   558             Log.e(LOGTAG, "JSON error", e);
   559         }
   560         GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:ShowHistory", json.toString());
   561         GeckoAppShell.sendEventToGecko(e);
   562         return true;
   563     }
   565     public void doStop() {
   566         GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Stop", "");
   567         GeckoAppShell.sendEventToGecko(e);
   568     }
   570     // Our version of nsSHistory::GetCanGoForward
   571     public boolean canDoForward() {
   572         return mHistoryIndex < mHistorySize - 1;
   573     }
   575     public boolean doForward() {
   576         if (!canDoForward())
   577             return false;
   579         GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Forward", "");
   580         GeckoAppShell.sendEventToGecko(e);
   581         return true;
   582     }
   584     void handleSessionHistoryMessage(String event, JSONObject message) throws JSONException {
   585         if (event.equals("New")) {
   586             final String url = message.getString("url");
   587             mHistoryIndex++;
   588             mHistorySize = mHistoryIndex + 1;
   589         } else if (event.equals("Back")) {
   590             if (!canDoBack()) {
   591                 Log.w(LOGTAG, "Received unexpected back notification");
   592                 return;
   593             }
   594             mHistoryIndex--;
   595         } else if (event.equals("Forward")) {
   596             if (!canDoForward()) {
   597                 Log.w(LOGTAG, "Received unexpected forward notification");
   598                 return;
   599             }
   600             mHistoryIndex++;
   601         } else if (event.equals("Goto")) {
   602             int index = message.getInt("index");
   603             if (index < 0 || index >= mHistorySize) {
   604                 Log.w(LOGTAG, "Received unexpected history-goto notification");
   605                 return;
   606             }
   607             mHistoryIndex = index;
   608         } else if (event.equals("Purge")) {
   609             int numEntries = message.getInt("numEntries");
   610             if (numEntries > mHistorySize) {
   611                 Log.w(LOGTAG, "Received unexpectedly large number of history entries to purge");
   612                 mHistoryIndex = -1;
   613                 mHistorySize = 0;
   614                 return;
   615             }
   617             mHistorySize -= numEntries;
   618             mHistoryIndex -= numEntries;
   620             // If we weren't at the last history entry, mHistoryIndex may have become too small
   621             if (mHistoryIndex < -1)
   622                 mHistoryIndex = -1;
   623         }
   624     }
   626     void handleLocationChange(JSONObject message) throws JSONException {
   627         final String uri = message.getString("uri");
   628         final String oldUrl = getURL();
   629         final boolean sameDocument = message.getBoolean("sameDocument");
   630         mEnteringReaderMode = ReaderModeUtils.isEnteringReaderMode(oldUrl, uri);
   632         if (!TextUtils.equals(oldUrl, uri)) {
   633             updateURL(uri);
   634             updateBookmark();
   635             if (!sameDocument) {
   636                 // We can unconditionally clear the favicon and title here: we
   637                 // already filtered both cases in which this was a (pseudo-)
   638                 // spurious location change, so we're definitely loading a new
   639                 // page.
   640                 clearFavicon();
   641                 updateTitle(null);
   642             }
   643         }
   645         if (sameDocument) {
   646             // We can get a location change event for the same document with an anchor tag
   647             // Notify listeners so that buttons like back or forward will update themselves
   648             Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, oldUrl);
   649             return;
   650         }
   652         setContentType(message.getString("contentType"));
   653         updateUserSearch(message.getString("userSearch"));
   654         mBaseDomain = message.optString("baseDomain");
   656         setHasFeeds(false);
   657         setHasOpenSearch(false);
   658         updateIdentityData(null);
   659         setReaderEnabled(false);
   660         setZoomConstraints(new ZoomConstraints(true));
   661         setHasTouchListeners(false);
   662         setBackgroundColor(DEFAULT_BACKGROUND_COLOR);
   663         setErrorType(ErrorType.NONE);
   664         setLoadProgressIfLoading(LOAD_PROGRESS_LOCATION_CHANGE);
   666         Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, oldUrl);
   667     }
   669     private static boolean shouldShowProgress(final String url) {
   670         return !AboutPages.isAboutPage(url);
   671     }
   673     void handleDocumentStart(boolean restoring, String url) {
   674         setLoadProgress(LOAD_PROGRESS_START);
   675         setState((!restoring && shouldShowProgress(url)) ? STATE_LOADING : STATE_SUCCESS);
   676         updateIdentityData(null);
   677         setReaderEnabled(false);
   678     }
   680     void handleDocumentStop(boolean success) {
   681         setState(success ? STATE_SUCCESS : STATE_ERROR);
   683         final String oldURL = getURL();
   684         final Tab tab = this;
   685         tab.setLoadProgress(LOAD_PROGRESS_STOP);
   686         ThreadUtils.getBackgroundHandler().postDelayed(new Runnable() {
   687             @Override
   688             public void run() {
   689                 // tab.getURL() may return null
   690                 if (!TextUtils.equals(oldURL, getURL()))
   691                     return;
   693                 ThumbnailHelper.getInstance().getAndProcessThumbnailFor(tab);
   694             }
   695         }, 500);
   696     }
   698     void handleContentLoaded() {
   699         setLoadProgressIfLoading(LOAD_PROGRESS_LOADED);
   700     }
   702     protected void saveThumbnailToDB() {
   703         final BitmapDrawable thumbnail = mThumbnail;
   704         if (thumbnail == null) {
   705             return;
   706         }
   708         try {
   709             String url = getURL();
   710             if (url == null) {
   711                 return;
   712             }
   714             BrowserDB.updateThumbnailForUrl(getContentResolver(), url, thumbnail);
   715         } catch (Exception e) {
   716             // ignore
   717         }
   718     }
   720     public void addPluginView(View view) {
   721         mPluginViews.add(view);
   722     }
   724     public void removePluginView(View view) {
   725         mPluginViews.remove(view);
   726     }
   728     public View[] getPluginViews() {
   729         return mPluginViews.toArray(new View[mPluginViews.size()]);
   730     }
   732     public void addPluginLayer(Object surfaceOrView, Layer layer) {
   733         synchronized(mPluginLayers) {
   734             mPluginLayers.put(surfaceOrView, layer);
   735         }
   736     }
   738     public Layer getPluginLayer(Object surfaceOrView) {
   739         synchronized(mPluginLayers) {
   740             return mPluginLayers.get(surfaceOrView);
   741         }
   742     }
   744     public Collection<Layer> getPluginLayers() {
   745         synchronized(mPluginLayers) {
   746             return new ArrayList<Layer>(mPluginLayers.values());
   747         }
   748     }
   750     public Layer removePluginLayer(Object surfaceOrView) {
   751         synchronized(mPluginLayers) {
   752             return mPluginLayers.remove(surfaceOrView);
   753         }
   754     }
   756     public int getBackgroundColor() {
   757         return mBackgroundColor;
   758     }
   760     /** Sets a new color for the background. */
   761     public void setBackgroundColor(int color) {
   762         mBackgroundColor = color;
   763     }
   765     /** Parses and sets a new color for the background. */
   766     public void setBackgroundColor(String newColor) {
   767         setBackgroundColor(parseColorFromGecko(newColor));
   768     }
   770     // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color
   771     // cannot be parsed, returns white.
   772     private static int parseColorFromGecko(String string) {
   773         if (sColorPattern == null) {
   774             sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)");
   775         }
   777         Matcher matcher = sColorPattern.matcher(string);
   778         if (!matcher.matches()) {
   779             return Color.WHITE;
   780         }
   782         int r = Integer.parseInt(matcher.group(1));
   783         int g = Integer.parseInt(matcher.group(2));
   784         int b = Integer.parseInt(matcher.group(3));
   785         return Color.rgb(r, g, b);
   786     }
   788     public void setDesktopMode(boolean enabled) {
   789         mDesktopMode = enabled;
   790     }
   792     public boolean getDesktopMode() {
   793         return mDesktopMode;
   794     }
   796     public boolean isPrivate() {
   797         return false;
   798     }
   800     /**
   801      * Sets the tab load progress to the given percentage.
   802      *
   803      * @param progressPercentage Percentage to set progress to (0-100)
   804      */
   805     void setLoadProgress(int progressPercentage) {
   806         mLoadProgress = progressPercentage;
   807     }
   809     /**
   810      * Sets the tab load progress to the given percentage only if the tab is
   811      * currently loading.
   812      *
   813      * about:neterror can trigger a STOP before other page load events (bug
   814      * 976426), so any post-START events should make sure the page is loading
   815      * before updating progress.
   816      *
   817      * @param progressPercentage Percentage to set progress to (0-100)
   818      */
   819     void setLoadProgressIfLoading(int progressPercentage) {
   820         if (getState() == STATE_LOADING) {
   821             setLoadProgress(progressPercentage);
   822         }
   823     }
   825     /**
   826      * Gets the tab load progress percentage.
   827      *
   828      * @return Current progress percentage
   829      */
   830     public int getLoadProgress() {
   831         return mLoadProgress;
   832     }
   834     public void setRecording(boolean isRecording) {
   835         if (isRecording) {
   836             mRecordingCount++;
   837         } else {
   838             mRecordingCount--;
   839         }
   840     }
   842     public boolean isRecording() {
   843         return mRecordingCount > 0;
   844     }
   845 }

mercurial