mobile/android/base/Tab.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial