diff --git a/globalhistory.py b/globalhistory.py
new file mode 100644
index 0000000..811de7e
--- /dev/null
+++ b/globalhistory.py
@@ -0,0 +1,78 @@
+# Copyright (C) 2008, Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+from datetime import datetime
+
+from xpcom import components
+from xpcom.components import interfaces
+from xpcom.server.factory import Factory
+
+import places
+
+class GlobalHistory:
+    _com_interfaces_ = interfaces.nsIGlobalHistory, \
+                       interfaces.nsIGlobalHistory2, \
+                       interfaces.nsIGlobalHistory3
+
+    cid = '{2a53cf28-c48e-4a01-ba18-3d3fef3e2985}'
+    description = 'Sugar Global History'
+
+    def __init__(self):
+        self._store = places.get_store()
+
+    def addPage(self, url):
+        self.addURI(url, False, True, None)
+
+    def isVisited(self, uri):
+        place = self._store.lookup_place(uri.spec)
+        return place != None
+
+    def addURI(self, uri, redirect, toplevel, referrer):
+        place = self._store.lookup_place(uri.spec)
+        if place:
+            place.visits += 1
+            place.last_visit = datetime.now()
+            self._store.update_place(place)            
+        else:
+            place = places.Place(uri.spec)
+            self._store.add_place(place)
+
+    def setPageTitle(self, uri, title):
+        place = self._store.lookup_place(uri.spec)
+        if place:
+            place.title = title
+            self._store.update_place(place)        
+
+    def addDocumentRedirect(self, old_channel, new_channel, flags, toplevel):
+        pass
+
+    def getURIGeckoFlags(self, uri):
+        place = self._store.lookup_place(uri.spec)
+        if place:
+            return place.gecko_flags
+        else:
+            return 0
+
+    def setURIGeckoFlags(self, uri, flags):
+        place = self._store.lookup_place(uri.spec)
+        if place:
+            place.gecko_flags = flags
+            self._store.update_place(place)        
+
+components.registrar.registerFactory(GlobalHistory.cid,
+                                     GlobalHistory.description,
+                                     '@mozilla.org/browser/global-history;2',
+                                     Factory(GlobalHistory))
diff --git a/places.py b/places.py
new file mode 100644
index 0000000..2850dee
--- /dev/null
+++ b/places.py
@@ -0,0 +1,125 @@
+# Copyright (C) 2008, Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import os
+import sqlite3
+from datetime import datetime, timedelta
+
+from sugar.activity import activity
+
+_store = None
+
+class Place(object):
+    def __init__(self, uri=None):
+        self.uri = uri
+        self.title = None
+        self.gecko_flags = 0
+        self.visits = 0
+        self.last_visit = datetime.now()
+
+class SqliteStore(object):
+    MAX_SEARCH_MATCHES = 20
+    EXPIRE_DAYS = 30
+    
+    def __init__(self):
+        db_path = os.path.join(activity.get_activity_root(),
+                               'data', 'places.db')
+
+        self._con = sqlite3.connect(db_path)
+        cur = self._con.cursor()
+
+        cur.execute('select * from sqlite_master where name == "places"')
+        if cur.fetchone() == None:
+            cur.execute("""create table places (
+                             uri         text,
+                             title       text,
+                             gecko_flags integer,
+                             visits      integer,
+                             last_visit  timestamp
+                           );
+                        """)
+        else:
+            self._cleanup()
+
+    def search(self, text):
+        cur = self._con.cursor()
+
+        text = '%' + text + '%'
+        cur.execute('select * from places where uri like ? or title like ? ' \
+                    'order by visits desc limit 0, ?',
+                    (text, text, self.MAX_SEARCH_MATCHES))
+
+        result = [self._place_from_row(row) for row in cur]
+
+        cur.close()
+
+        return result
+
+    def add_place(self, place):
+        cur = self._con.cursor()
+
+        cur.execute('insert into places values (?, ?, ?, ?, ?)', \
+                    (place.uri, place.title, place.gecko_flags,
+                     place.visits, place.last_visit))
+
+        self._con.commit()
+        cur.close()
+
+    def lookup_place(self, uri):
+        cur = self._con.cursor()
+        cur.execute('select * from places where uri=?', (uri,))
+
+        row = cur.fetchone()
+        if row:
+            return self._place_from_row(row)
+        else:
+            return None
+
+        cur.close()
+
+    def update_place(self, place):
+        cur = self._con.cursor()
+
+        cur.execute('update places set title=?, gecko_flags=?, '
+                    'visits=?, last_visit=? where uri=?',
+                    (place.title, place.gecko_flags, place.visits,
+                     place.last_visit, place.uri))
+
+        self._con.commit()
+        cur.close()
+
+    def _place_from_row(self, row):
+        place = Place()
+
+        place.uri, place.title, place.gecko_flags, \
+            place.visits, place.last_visit = row
+
+        return place
+
+    def _cleanup(self):
+        cur = self._con.cursor()
+
+        date = datetime.now() - timedelta(days=self.EXPIRE_DAYS)
+        cur.execute('delete from places where last_visit < ?', (date,))
+
+        self._con.commit()
+        cur.close()
+
+def get_store():
+    global _store
+    if _store == None:
+        _store = SqliteStore()
+    return _store
diff --git a/webactivity.py b/webactivity.py
index 4b4544a..4b2a2d8 100755
--- a/webactivity.py
+++ b/webactivity.py
@@ -71,6 +71,7 @@ import downloadmanager
 import sessionhistory 
 import progresslistener
 import filepicker
+import globalhistory
 
 _LIBRARY_PATH = '/usr/share/library-common/index.html'
 
diff --git a/webtoolbar.py b/webtoolbar.py
index 8987a37..2d7a92f 100755
--- a/webtoolbar.py
+++ b/webtoolbar.py
@@ -16,9 +16,11 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 from gettext import gettext as _
+import logging
 
 import gobject
 import gtk
+import pango
 from xpcom.components import interfaces
 from xpcom import components
 
@@ -29,9 +31,172 @@ from sugar._sugarext import AddressEntry
 import sessionhistory
 import progresslistener
 import filepicker
+import places
 
 _MAX_HISTORY_ENTRIES = 15
 
+class WebEntry(AddressEntry):
+    COL_ADDRESS = 0
+    COL_TITLE = 1
+
+    def __init__(self):
+        gobject.GObject.__init__(self)
+
+        self._popup_hid = None
+        self._search_view = self._search_create_view()
+
+        self._search_window = gtk.Window(gtk.WINDOW_POPUP)
+        self._search_window.add(self._search_view)
+        self._search_view.show()
+
+        self.connect('focus-in-event', self.__focus_in_event_cb)
+        self.connect('populate-popup', self.__populate_popup_cb)
+        self.connect('key-press-event', self.__key_press_event_cb)
+        self._focus_out_hid = self.connect(
+                    'focus-out-event', self.__focus_out_event_cb)
+        self._change_hid = self.connect('changed', self.__changed_cb)
+
+    def activate(self, uri):
+        self.handler_block(self._change_hid)
+        self.props.text = uri 
+        self.handler_unblock(self._change_hid)
+
+        self._search_popdown()
+        self.emit('activate')
+
+    def _set_address(self, address):
+        self.handler_block(self._change_hid)
+        self._address = address
+        self.handler_unblock(self._change_hid)
+
+    address = gobject.property(type=str, setter=_set_address)
+
+    def _set_title(self, title):
+        self.handler_block(self._change_hid)
+        self._title = title
+        self.handler_unblock(self._change_hid)
+
+    title = gobject.property(type=str, setter=_set_title)
+
+    def _search_create_view(self):
+        view = gtk.TreeView()
+        view.props.headers_visible=False
+
+        view.connect('button-press-event', self.__view_button_press_event_cb)
+
+        column = gtk.TreeViewColumn()
+        view.append_column(column)
+
+        cell = gtk.CellRendererText()
+        cell.props.ellipsize = pango.ELLIPSIZE_END
+        cell.props.ellipsize_set = True
+        column.pack_start(cell, True)
+
+        column.set_attributes(cell, text=self.COL_ADDRESS)
+
+        cell = gtk.CellRendererText()
+        cell.props.ellipsize = pango.ELLIPSIZE_END
+        cell.props.ellipsize_set = True
+        cell.props.alignment = pango.ALIGN_LEFT
+        cell.props.font = 'Bold'
+        column.pack_start(cell)
+
+        column.set_attributes(cell, text=self.COL_TITLE)
+
+        return view
+
+    def _search_update(self):
+        list_store = gtk.ListStore(str, str)
+
+        for place in places.get_store().search(self.props.text):
+            list_store.append([place.uri, place.title])
+
+        self._search_view.set_model(list_store)
+
+        return len(list_store) > 0
+
+    def _search_popup(self):
+        entry_x, entry_y = self.window.get_origin()
+        entry_w, entry_h = self.size_request()
+
+        x = entry_x
+        y = entry_y + entry_h
+        width = self.allocation.width
+        height = gtk.gdk.screen_height() / 3
+
+        i = self._search_view.get_model().get_iter_first()
+        self._search_view.get_selection().select_iter(i)
+
+        self._search_window.move(x, y)
+        self._search_window.resize(width, height)
+        self._search_window.show()
+
+    def _search_popdown(self):
+        self._search_window.hide()
+
+    def __focus_in_event_cb(self, entry, event):
+        self.handler_block(self._change_hid)
+        self.props.text = self._address
+        self.handler_unblock(self._change_hid)
+
+        self._search_popdown()
+
+    def __focus_out_event_cb(self, entry, event):
+        self.handler_block(self._change_hid)
+        self.props.text = self._title
+        self.handler_unblock(self._change_hid)
+
+        self._search_popdown()
+
+    def __view_button_press_event_cb(self, view, event):
+        model = view.get_model()
+
+        path, col_dummy, x_dummy, y_dummy = \
+                    view.get_path_at_pos(event.x, event.y)
+        if path:
+            uri = model[path][self.COL_ADDRESS]
+            self.activate(uri)
+
+    def __key_press_event_cb(self, entry, event):
+        keyname = gtk.gdk.keyval_name(event.keyval)
+
+        selection = self._search_view.get_selection()
+        model, selected = selection.get_selected()
+        if selected == None:
+            return False
+
+        if keyname == 'Up':
+            index = model.get_path(selected)[0]
+            if index > 0:
+                selection.select_path(index - 1)
+            return True
+        elif keyname == 'Down':
+            next = model.iter_next(selected)
+            if next:
+                selection.select_iter(next)
+            return True
+        elif keyname == 'Return':
+            uri = model[model.get_path(selected)][self.COL_ADDRESS]
+            self.activate(uri)
+            return True
+
+        return False
+
+    def __popup_unmap_cb(self, entry):
+        self.handler_unblock(self._focus_out_hid)
+
+    def __populate_popup_cb(self, entry, menu):
+        self.handler_block(self._focus_out_hid)
+        self.__popup_hid = menu.connect('unmap', self.__popup_unmap_cb)
+
+    def __changed_cb(self, entry):
+        self._address = self.props.text
+
+        if not self.props.text or not self._search_update():
+            self._search_popdown()
+        else:
+            self._search_popup()
+
 class WebToolbar(gtk.Toolbar):
     __gtype_name__ = 'WebToolbar'
 
@@ -67,7 +232,7 @@ class WebToolbar(gtk.Toolbar):
         self.insert(self._stop_and_reload, -1)
         self._stop_and_reload.show()
 
-        self._entry = AddressEntry()
+        self._entry = WebEntry()
         self._entry.connect('activate', self._entry_activate_cb)
 
         entry_item = gtk.ToolItem()
