File: GlobalHistory.java

package info (click to toggle)
wine-gecko-2.24 2.24%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 740,092 kB
  • ctags: 688,789
  • sloc: cpp: 3,160,639; ansic: 1,619,153; python: 164,084; java: 128,022; asm: 114,527; xml: 69,863; sh: 55,281; makefile: 49,648; perl: 20,454; objc: 2,344; yacc: 2,066; pascal: 995; lex: 982; exp: 449; php: 244; lisp: 228; awk: 211; sed: 61; csh: 21; ada: 16; ruby: 3
file content (151 lines) | stat: -rw-r--r-- 5,582 bytes parent folder | download | duplicates (4)
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);
            }
        });
    }
}