1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.util.ThreadUtils;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import java.lang.ref.SoftReference;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
class GlobalHistory {
private static final String LOGTAG = "GeckoGlobalHistory";
private static GlobalHistory sInstance = new GlobalHistory();
static GlobalHistory getInstance() {
return sInstance;
}
// this is the delay between receiving a URI check request and processing it.
// this allows batching together multiple requests and processing them together,
// which is more efficient.
private static final long BATCHING_DELAY_MS = 100;
private final Handler mHandler; // a background thread on which we can process requests
private final Queue<String> mPendingUris; // URIs that need to be checked
private SoftReference<Set<String>> mVisitedCache; // cache of the visited URI list
private final Runnable mNotifierRunnable; // off-thread runnable used to check URIs
private boolean mProcessing; // = false // whether or not the runnable is queued/working
private GlobalHistory() {
mHandler = ThreadUtils.getBackgroundHandler();
mPendingUris = new LinkedList<String>();
mVisitedCache = new SoftReference<Set<String>>(null);
mNotifierRunnable = new Runnable() {
@Override
public void run() {
Set<String> visitedSet = mVisitedCache.get();
if (visitedSet == null) {
// the cache was wiped away, repopulate it
Log.w(LOGTAG, "Rebuilding visited link set...");
visitedSet = new HashSet<String>();
Cursor c = BrowserDB.getAllVisitedHistory(GeckoAppShell.getContext().getContentResolver());
if (c.moveToFirst()) {
do {
visitedSet.add(c.getString(0));
} while (c.moveToNext());
}
mVisitedCache = new SoftReference<Set<String>>(visitedSet);
c.close();
}
// this runs on the same handler thread as the checkUriVisited code,
// so no synchronization needed
while (true) {
String uri = mPendingUris.poll();
if (uri == null) {
break;
}
if (visitedSet.contains(uri)) {
GeckoAppShell.notifyUriVisited(uri);
}
}
mProcessing = false;
}
};
}
public void addToGeckoOnly(String uri) {
Set<String> visitedSet = mVisitedCache.get();
if (visitedSet != null) {
visitedSet.add(uri);
}
GeckoAppShell.notifyUriVisited(uri);
}
// Logic ported from nsNavHistory::CanAddURI.
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/places/nsNavHistory.cpp#1272
private boolean canAddURI(String uri) {
if (uri == null || uri.length() == 0)
return false;
// First, heck the most common cases (HTTP, HTTPS) to avoid most of the work.
if (uri.startsWith("http:") || uri.startsWith("https:"))
return true;
String scheme = Uri.parse(uri).getScheme();
if (scheme == null)
return false;
// Now check for all bad things.
if (scheme.equals("about") ||
scheme.equals("imap") ||
scheme.equals("news") ||
scheme.equals("mailbox") ||
scheme.equals("moz-anno") ||
scheme.equals("view-source") ||
scheme.equals("chrome") ||
scheme.equals("resource") ||
scheme.equals("data") ||
scheme.equals("wyciwyg") ||
scheme.equals("javascript"))
return false;
return true;
}
public void add(String uri) {
if (!canAddURI(uri))
return;
BrowserDB.updateVisitedHistory(GeckoAppShell.getContext().getContentResolver(), uri);
addToGeckoOnly(uri);
}
public void update(String uri, String title) {
if (!canAddURI(uri))
return;
BrowserDB.updateHistoryTitle(GeckoAppShell.getContext().getContentResolver(), uri, title);
}
public void checkUriVisited(final String uri) {
mHandler.post(new Runnable() {
@Override
public void run() {
// this runs on the same handler thread as the processing loop,
// so no synchronization needed
mPendingUris.add(uri);
if (mProcessing) {
// there's already a runnable queued up or working away, so
// no need to post another
return;
}
mProcessing = true;
mHandler.postDelayed(mNotifierRunnable, BATCHING_DELAY_MS);
}
});
}
}
|