mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-10 02:08:38 +09:00
155 lines
4.8 KiB
Swift
155 lines
4.8 KiB
Swift
/* 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 Foundation
|
|
import Shared
|
|
import Storage
|
|
import XCGLogger
|
|
import SwiftyJSON
|
|
|
|
// Int64.max / 1000.
|
|
private let MaxSecondsToConvertInt64: Int64 = 9223372036854775
|
|
private let MaxSecondsToConvertDouble: Double = Double(9223372036854775 as Int64)
|
|
|
|
private let log = Logger.browserLogger
|
|
|
|
open class TabsPayload: CleartextPayloadJSON {
|
|
open class Tab {
|
|
let title: String
|
|
let urlHistory: [String]
|
|
let lastUsed: Timestamp
|
|
let icon: String?
|
|
|
|
fileprivate init(title: String, urlHistory: [String], lastUsed: Timestamp, icon: String?) {
|
|
self.title = title
|
|
self.urlHistory = urlHistory
|
|
self.lastUsed = lastUsed
|
|
self.icon = icon
|
|
}
|
|
|
|
func toRemoteTabForClient(_ guid: GUID) -> RemoteTab? {
|
|
let urls = urlHistory.flatMap({ $0.asURL })
|
|
if urls.isEmpty {
|
|
log.debug("Bug 1201875 - Discarding tab as history has no conforming URLs.")
|
|
return nil
|
|
}
|
|
|
|
return RemoteTab(clientGUID: guid, URL: urls[0], title: self.title, history: urls, lastUsed: self.lastUsed, icon: self.icon?.asURL)
|
|
}
|
|
|
|
class func remoteTabFromJSON(_ json: JSON, clientGUID: GUID) -> RemoteTab? {
|
|
return fromJSON(json)?.toRemoteTabForClient(clientGUID)
|
|
}
|
|
|
|
class func fromJSON(_ json: JSON) -> Tab? {
|
|
func getLastUsed(_ json: JSON) -> Timestamp? {
|
|
let lastUsed = json["lastUsed"]
|
|
if lastUsed.isBool() {
|
|
return nil
|
|
}
|
|
// This might be a string or a number.
|
|
if let num = lastUsed.int64 {
|
|
if num < 0 {
|
|
// Timestamps are unsigned.
|
|
return nil
|
|
}
|
|
if num > MaxSecondsToConvertInt64 {
|
|
// This will overflow when multiplied.
|
|
return nil
|
|
}
|
|
return Timestamp(num * 1000)
|
|
}
|
|
|
|
if let num = lastUsed.double {
|
|
if num < 0 {
|
|
// Timestamps are unsigned.
|
|
return nil
|
|
}
|
|
if num > MaxSecondsToConvertDouble {
|
|
// This will overflow when multiplied.
|
|
return nil
|
|
}
|
|
return Timestamp(num * 1000)
|
|
}
|
|
|
|
if let num = lastUsed.string {
|
|
// Try parsing.
|
|
return someKindOfTimestampStringToTimestamp(num)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
if let title = json["title"].string,
|
|
let urlHistory = jsonsToStrings(json["urlHistory"].array),
|
|
let lastUsed = getLastUsed(json) {
|
|
return Tab(title: title, urlHistory: urlHistory, lastUsed: lastUsed, icon: json["icon"].string)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
override open func isValid() -> Bool {
|
|
if !super.isValid() {
|
|
return false
|
|
}
|
|
|
|
if self["deleted"].bool ?? false {
|
|
return true
|
|
}
|
|
|
|
return self["clientName"].isString() &&
|
|
self["tabs"].isArray()
|
|
}
|
|
|
|
// Eventually it'd be nice to unify RemoteTab and Tab. We want to kill the GUID in RemoteTab,
|
|
// at which point the only distinction between the two is that RemoteTab is "simple" and
|
|
// lives in Storage, and Tab is more closely tied to TabsPayload.
|
|
|
|
var remoteTabs: [RemoteTab] {
|
|
if let clientGUID = self["id"].string {
|
|
let payloadTabs = self["tabs"].arrayValue
|
|
let remoteTabs = payloadTabs.flatMap({ Tab.remoteTabFromJSON($0, clientGUID: clientGUID) })
|
|
if payloadTabs.count != remoteTabs.count {
|
|
log.debug("Bug 1201875 - Missing remote tabs from sync")
|
|
}
|
|
return remoteTabs
|
|
}
|
|
log.debug("no client ID for remote tabs")
|
|
return []
|
|
}
|
|
|
|
var tabs: [Tab] {
|
|
return self["tabs"].arrayValue.flatMap(Tab.fromJSON)
|
|
}
|
|
|
|
var clientName: String {
|
|
return self["clientName"].string!
|
|
}
|
|
|
|
override open func equalPayloads(_ obj: CleartextPayloadJSON) -> Bool {
|
|
if !(obj is TabsPayload) {
|
|
return false
|
|
}
|
|
|
|
if !super.equalPayloads(obj) {
|
|
return false
|
|
}
|
|
|
|
let p = obj as! TabsPayload
|
|
if p.clientName != self.clientName {
|
|
return false
|
|
}
|
|
|
|
// TODO: compare tabs.
|
|
/*
|
|
if p.tabs != self.tabs {
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
return true
|
|
}
|
|
}
|