Skip to content

Commit

Permalink
Bug 1159562, Bug 1159563, Bug 1159561 — History rework and minor fixe…
Browse files Browse the repository at this point in the history
…s. r=nalexander,st3fan
  • Loading branch information
rnewman committed Apr 29, 2015
2 parents 9119ed9 + 66e7a53 commit 26bb508
Show file tree
Hide file tree
Showing 25 changed files with 308 additions and 139 deletions.
6 changes: 6 additions & 0 deletions Client.xcodeproj/project.pbxproj
Expand Up @@ -69,6 +69,8 @@
282731A01ABC9C5900AA1954 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FEBABAE1AB3659000DB5728 /* ResultTests.swift */; };
282731A21ABC9D2600AA1954 /* Prefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 282731A11ABC9D2600AA1954 /* Prefs.swift */; };
282DA4731A68C1E700A406E2 /* OpenSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3FA77831A43B2CE0010CD32 /* OpenSearch.swift */; };
28302E401AF0747800521E2E /* DatabaseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28302E3F1AF0747800521E2E /* DatabaseError.swift */; };
283A2AD91AF0794D003C0A26 /* DatabaseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28302E3F1AF0747800521E2E /* DatabaseError.swift */; };
284AA3AD1AD4C386009041C5 /* Clients.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C4AB711AD42D4300D9ACE3 /* Clients.swift */; };
2853C5411AD84C6800C4F31D /* TabsPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2853C5401AD84C6800C4F31D /* TabsPayload.swift */; };
2853C5421AD84C6800C4F31D /* TabsPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2853C5401AD84C6800C4F31D /* TabsPayload.swift */; };
Expand Down Expand Up @@ -1074,6 +1076,7 @@
282731681ABC9BE700AA1954 /* SyncTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SyncTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
282731701ABC9BE700AA1954 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
282731A11ABC9D2600AA1954 /* Prefs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Prefs.swift; sourceTree = "<group>"; };
28302E3F1AF0747800521E2E /* DatabaseError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseError.swift; sourceTree = "<group>"; };
283306E81AB3BB87008999AC /* Functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Functions.swift; sourceTree = "<group>"; };
2853C5401AD84C6800C4F31D /* TabsPayload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsPayload.swift; sourceTree = "<group>"; };
28786E541AB0F5FA009EA9EF /* DeferredTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeferredTests.swift; path = SyncTests/DeferredTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1830,6 +1833,7 @@
2FCAE23F1ABB531100877008 /* Bookmarks.swift */,
28C4AB711AD42D4300D9ACE3 /* Clients.swift */,
2FCAE2411ABB531100877008 /* Cursor.swift */,
28302E3F1AF0747800521E2E /* DatabaseError.swift */,
2FCAE2421ABB531100877008 /* Favicons.swift */,
2FCAE2431ABB531100877008 /* FileAccessor.swift */,
2FCAE2441ABB531100877008 /* History.swift */,
Expand Down Expand Up @@ -3485,6 +3489,7 @@
2FCAE26C1ABB531100877008 /* JoinedFaviconsHistoryTable.swift in Sources */,
2FCAE2771ABB531100877008 /* SwiftData.swift in Sources */,
2FCAE25F1ABB531100877008 /* Cursor.swift in Sources */,
28302E401AF0747800521E2E /* DatabaseError.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -3527,6 +3532,7 @@
2FCAE2881ABB533A00877008 /* TestHistoryTable.swift in Sources */,
2FA4351F1ABB6831008031D1 /* HistoryTable.swift in Sources */,
2FA435191ABB6829008031D1 /* RemoteTabs.swift in Sources */,
283A2AD91AF0794D003C0A26 /* DatabaseError.swift in Sources */,
284AA3AD1AD4C386009041C5 /* Clients.swift in Sources */,
2FA4352A1ABB6831008031D1 /* VisitsTable.swift in Sources */,
2FA435111ABB6829008031D1 /* Bookmarks.swift in Sources */,
Expand Down
9 changes: 4 additions & 5 deletions Client/Frontend/Browser/SearchLoader.swift
Expand Up @@ -34,14 +34,13 @@ class _SearchLoader<UnusedA, UnusedB>: Loader<Cursor, SearchViewController> {
}

let options = QueryOptions(filter: query, filterType: FilterType.Url, sort: QuerySort.Frecency)
self.history.get(options, complete: { (cursor: Cursor) in
if cursor.status != .Success {
println("Err: \(cursor.statusMessage)")
} else {
self.history.get(options).uponQueue(dispatch_get_main_queue()) { result in
// Failed cursors are excluded in .get().
if let cursor = result.successValue {
self.load(cursor)
self.urlBar.setAutocompleteSuggestion(self.getAutocompleteSuggestion(cursor))
}
})
}
}
}

Expand Down
24 changes: 14 additions & 10 deletions Client/Frontend/Home/HistoryPanel.swift
Expand Up @@ -4,6 +4,7 @@

import UIKit

import Shared
import Storage

private func getDate(#dayOffset: Int) -> NSDate {
Expand All @@ -26,11 +27,14 @@ class HistoryPanel: SiteTableViewController, HomePanel {
override func reloadData() {
let opts = QueryOptions()
opts.sort = .LastVisit
profile.history.get(opts, complete: { (data: Cursor) -> Void in
self.sectionOffsets = [Int: Int]()
self.data = data
self.tableView.reloadData()
})
profile.history.get(opts).uponQueue(dispatch_get_main_queue()) { result in
if let data = result.successValue {
self.sectionOffsets = [Int: Int]()
self.data = data
self.tableView.reloadData()
}
// TODO: error handling.
}
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
Expand Down Expand Up @@ -80,15 +84,15 @@ class HistoryPanel: SiteTableViewController, HomePanel {
return title.uppercaseString
}

private func isInSection(date: NSDate, section: Int) -> Bool {
let now = NSDate()
private func isInSection(date: MicrosecondTimestamp, section: Int) -> Bool {
let date = Double(date)
switch section {
case 0:
return date.timeIntervalSince1970 > Today.timeIntervalSince1970
return date > (1000 * Today.timeIntervalSince1970)
case 1:
return date.timeIntervalSince1970 > Yesterday.timeIntervalSince1970
return date > (1000 * Yesterday.timeIntervalSince1970)
case 2:
return date.timeIntervalSince1970 > ThisWeek.timeIntervalSince1970
return date > (1000 * ThisWeek.timeIntervalSince1970)
default:
return true
}
Expand Down
15 changes: 10 additions & 5 deletions Client/Frontend/Home/TopSitesPanel.swift
Expand Up @@ -25,11 +25,16 @@ class TopSitesPanel: UIViewController, UICollectionViewDelegate, HomePanel {
var profile: Profile! {
didSet {
let options = QueryOptions(filter: nil, filterType: .None, sort: .Frecency)
profile.history.get(options, complete: { (data) -> Void in
self.dataSource.data = data
self.dataSource.profile = self.profile
self.collection.reloadData()
})

// This needs to run on the main thread so that our dataSource is ready.
profile.history.get(options).uponQueue(dispatch_get_main_queue()) { result in
if let data = result.successValue {
self.dataSource.data = data
self.dataSource.profile = self.profile
self.collection.reloadData()
}
// TODO: error handling.
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions Client/Frontend/Settings/Clearables.swift
Expand Up @@ -26,9 +26,9 @@ class HistoryClearable : Clearable {
self.profile = profile
}

func clear() -> Deferred<Result<()>> {
let deferred = Deferred<Result<()>>()
profile.history.clear({ success in
func clear() -> Success {
let deferred = Success()
profile.history.clear().upon { success in
self.profile.thumbnails.clear({ success in
SDImageCache.sharedImageCache().clearDisk()
SDImageCache.sharedImageCache().clearMemory()
Expand All @@ -38,7 +38,7 @@ class HistoryClearable : Clearable {
return
})
})
})
}
return deferred
}
}
Expand Down
14 changes: 14 additions & 0 deletions ClientTests/ProfilePrefsTests.swift
Expand Up @@ -72,4 +72,18 @@ class TestProfilePrefs: ProfileTest {
XCTAssertNil(prefs.stringArrayForKey("key"))
}
}

func testMockProfilePrefsRoundtripsTimestamps() {
let prefs = MockProfilePrefs().branch("baz")
let val: Timestamp = NSDate.now()
prefs.setLong(val, forKey: "foobar")
XCTAssertEqual(val, prefs.unsignedLongForKey("foobar")!)
}

func testMockProfilePrefsKeys() {
let prefs = MockProfilePrefs().branch("baz") as! MockProfilePrefs
let val: Timestamp = NSDate.now()
prefs.setLong(val, forKey: "foobar")
XCTAssertEqual(val, (prefs.things["baz.foobar"] as! NSNumber).unsignedLongLongValue)
}
}
24 changes: 13 additions & 11 deletions ClientTests/TestHistory.swift
Expand Up @@ -7,9 +7,9 @@ class TestHistory : ProfileTest {
private func innerAddSite(history: BrowserHistory, url: String, title: String, callback: (success: Bool) -> Void) {
// Add an entry
let site = Site(url: url, title: title)
let visit = Visit(site: site, date: NSDate())
history.addVisit(visit) { success in
callback(success: success)
let visit = SiteVisit(site: site, date: NSDate.nowMicroseconds())
history.addVisit(visit).upon {
callback(success: $0.isSuccess)
}
}

Expand All @@ -23,9 +23,10 @@ class TestHistory : ProfileTest {

private func innerCheckSites(history: BrowserHistory, callback: (cursor: Cursor) -> Void) {
// Retrieve the entry
history.get(nil, complete: { cursor in
callback(cursor: cursor)
})
history.get(nil).upon {
XCTAssertTrue($0.isSuccess)
callback(cursor: $0.successValue!)
}
}


Expand All @@ -51,9 +52,7 @@ class TestHistory : ProfileTest {
}

private func innerClear(history: BrowserHistory, callback: (s: Bool) -> Void) {
history.clear({ success in
callback(s: success)
})
history.clear().upon { callback(s: $0.isSuccess) }
}

private func clear(history: BrowserHistory, s: Bool = true) {
Expand All @@ -69,10 +68,13 @@ class TestHistory : ProfileTest {

private func checkVisits(history: BrowserHistory, url: String) {
let expectation = self.expectationWithDescription("Wait for history")
history.get(nil) { cursor in
history.get(nil).upon { result in
XCTAssertTrue(result.isSuccess)
let options = QueryOptions()
options.filter = url
history.get(options) { cursor in
history.get(options).upon { result in
XCTAssertTrue(result.isSuccess)
let cursor = result.successValue!
XCTAssertEqual(cursor.status, CursorStatus.Success, "returned success \(cursor.statusMessage)")
// XXX - We don't allow querying much info about visits here anymore, so there isn't a lot to do
expectation.fulfill()
Expand Down
6 changes: 2 additions & 4 deletions Providers/Profile.swift
Expand Up @@ -136,10 +136,8 @@ public class BrowserProfile: Profile {
var site: Site!
if let title = notification.userInfo!["title"] as? NSString {
site = Site(url: url.absoluteString!, title: title as String)
let visit = Visit(site: site, date: NSDate())
history.addVisit(visit, complete: { (success) -> Void in
// nothing to do
})
let visit = SiteVisit(site: site, date: NSDate.nowMicroseconds())
history.addVisit(visit)
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions Storage/DatabaseError.swift
@@ -0,0 +1,20 @@
/* 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/. */

import Shared

/**
* Used to bridge the NSErrors we get here into something that Result is happy with.
*/
public class DatabaseError: ErrorType {
let err: NSError?

public var description: String {
return err?.localizedDescription ?? "Unknown database error."
}

init(err: NSError?) {
self.err = err
}
}
51 changes: 47 additions & 4 deletions Storage/History.swift
Expand Up @@ -14,9 +14,9 @@ public typealias GUID = String
* `clear` might or might not need to set a bunch of flags to upload deletions.
*/
public protocol BrowserHistory {
func clear(complete: (success: Bool) -> Void)
func get(options: QueryOptions?, complete: (data: Cursor) -> Void)
func addVisit(visit: Visit, complete: (success: Bool) -> Void)
func clear() -> Success
func get(options: QueryOptions?) -> Deferred<Result<Cursor>>
func addVisit(visit: SiteVisit) -> Success
}

/**
Expand All @@ -26,7 +26,50 @@ public protocol BrowserHistory {
public protocol SyncableHistory {
func ensurePlaceWithURL(url: String, hasGUID guid: GUID) -> Deferred<Result<()>>
func changeGUID(old: GUID, new: GUID) -> Deferred<Result<()>>
func deleteByGUID(guid: GUID, deletedAt: Timestamp) -> Deferred<Result<()>>

func insertOrReplaceRemoteVisits(visits: [Visit], forGUID guid: GUID) -> Deferred<Result<()>>
func insertOrUpdatePlaceWithURL(url: String, title: String, guid: GUID) -> Deferred<Result<GUID>>
func insertOrUpdatePlace(place: RemotePlace) -> Deferred<Result<GUID>>
}

// TODO: integrate Site with these.

public class Place {
public let guid: GUID
public let url: String
public let title: String

public init(guid: GUID, url: String, title: String) {
self.guid = guid
self.url = url
self.title = title
}
}

public class LocalPlace: Place {
// Local modification time.
public var modified: Timestamp

public init(guid: GUID, url: String, title: String, modified: Timestamp) {
self.modified = modified
super.init(guid: guid, url: url, title: title)
}
}

public class RemotePlace: Place {
// Server timestamp on the record.
public let modified: Timestamp

// Remote places are initially unapplied, and this is flipped when we reconcile them.
public var applied: Bool

public convenience init(guid: GUID, url: NSURL, title: String, modified: Timestamp) {
self.init(guid: guid, url: url.absoluteString!, title: title, modified: modified)
}

public init(guid: GUID, url: String, title: String, modified: Timestamp) {
self.applied = false
self.modified = modified
super.init(guid: guid, url: url, title: title)
}
}
5 changes: 3 additions & 2 deletions Storage/SQL/BrowserDB.swift
Expand Up @@ -41,10 +41,11 @@ let DBCouldNotOpenErrorCode = 200
// Version 3 - Added a favicons table
// Version 4 - Added a readinglist table
// Version 5 - Added the clients and the tabs tables.
// Version 6 - Visit timestamps are now microseconds.
public class BrowserDB {
private let db: SwiftData
// XXX: Increasing this should blow away old history, since we currently dont' support any upgrades
private let Version: Int = 5
// XXX: Increasing this should blow away old history, since we currently don't support any upgrades.
private let Version: Int = 6
private let FileName = "browser.db"
private let files: FileAccessor
private let schemaTable: SchemaTable<TableInfo>
Expand Down
13 changes: 7 additions & 6 deletions Storage/SQL/JoinedHistoryVisitsTable.swift
Expand Up @@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import Foundation
import Shared

let HistoryVisits = "history-visits"

Expand All @@ -20,7 +21,7 @@ func getFrecency() -> String {
// 2.) Adding a visit here will ensure that a site exists for the visit
// 3.) Updates currently only update site information.
class JoinedHistoryVisitsTable: Table {
typealias Type = (site: Site?, visit: Visit?)
typealias Type = (site: Site?, visit: SiteVisit?)
var name: String { return HistoryVisits }
var version: Int { return 1 }

Expand Down Expand Up @@ -94,7 +95,7 @@ class JoinedHistoryVisitsTable: Table {
}

// Now add a visit
let visit = Visit(site: site, date: NSDate())
let visit = SiteVisit(site: site, date: NSDate.nowMicroseconds())
return visits.insert(db, item: visit, err: &err)
}

Expand All @@ -109,12 +110,12 @@ class JoinedHistoryVisitsTable: Table {
if let visit = item?.visit {
return visits.delete(db, item: visit, err: &err)
} else if let site = item?.site {
let v = Visit(site: site, date: NSDate())
let v = SiteVisit(site: site, date: NSDate.nowMicroseconds())
visits.delete(db, item: v, err: &err)
return history.delete(db, item: site, err: &err)
} else if item == nil {
let site: Site? = nil
let visit: Visit? = nil
let visit: SiteVisit? = nil
history.delete(db, item: site, err: &err);
return visits.delete(db, item: visit, err: &err);
}
Expand All @@ -126,10 +127,10 @@ class JoinedHistoryVisitsTable: Table {
site.guid = result["guid"] as? String
site.id = result["historyId"] as? Int

let d = NSDate(timeIntervalSince1970: result["visitDate"] as! Double)
let d = (result["visitDate"] as! NSNumber).unsignedLongLongValue

// This visit is a combination of multiple visits. Type is meaningless.
let visit = Visit(site: site, date: d, type: VisitType.Unknown)
let visit = SiteVisit(site: site, date: d, type: VisitType.Unknown)
visit.id = result["visitId"] as? Int

site.latestVisit = visit
Expand Down

0 comments on commit 26bb508

Please sign in to comment.