Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/Adapters/GCDWebServer/GCDHTTPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public class GCDHTTPServer: HTTPServer, Loggable {
let pathWithoutAnchor = url.removingQuery().removingFragment()

for (endpoint, handler) in handlers {
if endpoint == pathWithoutAnchor {
if endpoint.isEquivalentTo(pathWithoutAnchor) {
let request = HTTPServerRequest(url: url, href: nil)
let resource = handler.onRequest(request)
completion(
Expand Down
2 changes: 1 addition & 1 deletion Sources/Adapters/GCDWebServer/ResourceResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class ResourceResponse: ReadiumGCDWebServerFileResponse, Loggable {

super.init()

contentType = resource.link.type ?? ""
contentType = resource.link.mediaType?.string ?? ""

// Disable HTTP caching for publication resources, because it poses a security threat for protected
// publications.
Expand Down
8 changes: 5 additions & 3 deletions Sources/Navigator/Audiobook/AudioNavigator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,8 @@ open class AudioNavigator: Navigator, Configurable, AudioSessionUser, Loggable {
}

return Locator(
href: link.href,
type: link.type ?? "audio/*",
href: link.url(),
mediaType: link.mediaType ?? MediaType("audio/*")!,
title: link.title,
locations: Locator.Locations(
fragments: ["t=\(time)"],
Expand Down Expand Up @@ -382,7 +382,9 @@ open class AudioNavigator: Navigator, Configurable, AudioSessionUser, Loggable {

@discardableResult
public func go(to locator: Locator, animated: Bool = false, completion: @escaping () -> Void = {}) -> Bool {
guard let newResourceIndex = publication.readingOrder.firstIndex(withHREF: locator.href) else {
let locator = publication.normalizeLocator(locator)

guard let newResourceIndex = publication.readingOrder.firstIndexWithHREF(locator.href) else {
return false
}
let link = publication.readingOrder[newResourceIndex]
Expand Down
28 changes: 13 additions & 15 deletions Sources/Navigator/Audiobook/PublicationMediaLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import ReadiumShared
///
/// Useful for local resources or when you need to customize the way HTTP requests are sent.
final class PublicationMediaLoader: NSObject, AVAssetResourceLoaderDelegate {
private typealias HREF = String

public enum AssetError: LocalizedError {
case invalidHREF(String)

Expand All @@ -35,10 +33,8 @@ final class PublicationMediaLoader: NSObject, AVAssetResourceLoaderDelegate {

/// Creates a new `AVURLAsset` to serve the given `link`.
func makeAsset(for link: Link) throws -> AVURLAsset {
guard
let originalURL = try? link.url(relativeTo: publication.baseURL),
var components = URLComponents(url: originalURL.url, resolvingAgainstBaseURL: true)
else {
let originalURL = link.url(relativeTo: publication.baseURL)
guard var components = URLComponents(url: originalURL.url, resolvingAgainstBaseURL: true) else {
throw AssetError.invalidHREF(link.href)
}

Expand All @@ -55,10 +51,11 @@ final class PublicationMediaLoader: NSObject, AVAssetResourceLoaderDelegate {

// MARK: - Resource Management

private var resources: [HREF: Resource] = [:]
private var resources: [AnyURL: Resource] = [:]

private func resource(forHREF href: HREF) -> Resource {
if let res = resources[href] {
private func resource<T: URLConvertible>(forHREF href: T) -> Resource {
let href = href.anyURL
if let res = resources[equivalent: href] {
return res
}

Expand All @@ -72,10 +69,11 @@ final class PublicationMediaLoader: NSObject, AVAssetResourceLoaderDelegate {
private typealias CancellableRequest = (request: AVAssetResourceLoadingRequest, cancellable: Cancellable)

/// List of on-going loading requests.
private var requests: [HREF: [CancellableRequest]] = [:]
private var requests: [AnyURL: [CancellableRequest]] = [:]

/// Adds a new loading request.
private func registerRequest(_ request: AVAssetResourceLoadingRequest, cancellable: Cancellable, for href: HREF) {
private func registerRequest<T: URLConvertible>(_ request: AVAssetResourceLoadingRequest, cancellable: Cancellable, for href: T) {
let href = href.anyURL
var reqs: [CancellableRequest] = requests[href] ?? []
reqs.append((request, cancellable))
requests[href] = reqs
Expand Down Expand Up @@ -129,7 +127,7 @@ final class PublicationMediaLoader: NSObject, AVAssetResourceLoaderDelegate {

private func fillInfo(_ infoRequest: AVAssetResourceLoadingContentInformationRequest, of request: AVAssetResourceLoadingRequest, using resource: Resource) {
infoRequest.isByteRangeAccessSupported = true
infoRequest.contentType = resource.link.mediaType.uti
infoRequest.contentType = resource.link.mediaType?.uti
if case let .success(length) = resource.length {
infoRequest.contentLength = Int64(length)
}
Expand All @@ -153,7 +151,7 @@ final class PublicationMediaLoader: NSObject, AVAssetResourceLoaderDelegate {
}
)

registerRequest(request, cancellable: cancellable, for: resource.link.href)
registerRequest(request, cancellable: cancellable, for: resource.link.url())
}

func resourceLoader(_ resourceLoader: AVAssetResourceLoader, didCancel loadingRequest: AVAssetResourceLoadingRequest) {
Expand All @@ -164,7 +162,7 @@ final class PublicationMediaLoader: NSObject, AVAssetResourceLoaderDelegate {
private let schemePrefix = "readium"

extension URL {
var audioHREF: String? {
var audioHREF: AnyURL? {
guard let url = absoluteURL, url.scheme.rawValue.hasPrefix(schemePrefix) == true else {
return nil
}
Expand All @@ -173,6 +171,6 @@ extension URL {
// * readium:relative/file.mp3
// * readiumfile:///directory/local-file.mp3
// * readiumhttp(s)://domain.com/external-file.mp3
return url.string.removingPrefix(schemePrefix).removingPrefix(":")
return AnyURL(string: url.string.removingPrefix(schemePrefix).removingPrefix(":"))
}
}
15 changes: 7 additions & 8 deletions Sources/Navigator/CBZ/CBZNavigatorViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ open class CBZNavigatorViewController: UIViewController, VisualNavigator, Loggab
self.publicationEndpoint = publicationEndpoint

initialIndex = {
guard let initialLocation = initialLocation, let initialIndex = publication.readingOrder.firstIndex(withHREF: initialLocation.href) else {
guard let initialLocation = initialLocation, let initialIndex = publication.readingOrder.firstIndexWithHREF(initialLocation.href) else {
return 0
}
return initialIndex
Expand Down Expand Up @@ -180,13 +180,10 @@ open class CBZNavigatorViewController: UIViewController, VisualNavigator, Loggab
}

private func imageViewController(at index: Int) -> ImageViewController? {
guard
publication.readingOrder.indices.contains(index),
let url = try? publication.readingOrder[index].url(relativeTo: publicationBaseURL)
else {
guard publication.readingOrder.indices.contains(index) else {
return nil
}

let url = publication.readingOrder[index].url(relativeTo: publicationBaseURL)
return ImageViewController(index: index, url: url.url)
}

Expand All @@ -209,14 +206,16 @@ open class CBZNavigatorViewController: UIViewController, VisualNavigator, Loggab
}

public func go(to locator: Locator, animated: Bool, completion: @escaping () -> Void) -> Bool {
guard let index = publication.readingOrder.firstIndex(withHREF: locator.href) else {
let locator = publication.normalizeLocator(locator)

guard let index = publication.readingOrder.firstIndexWithHREF(locator.href) else {
return false
}
return goToResourceAtIndex(index, animated: animated, isJump: true, completion: completion)
}

public func go(to link: Link, animated: Bool, completion: @escaping () -> Void) -> Bool {
guard let index = publication.readingOrder.firstIndex(withHREF: link.href) else {
guard let index = publication.readingOrder.firstIndexWithHREF(link.url()) else {
return false
}
return goToResourceAtIndex(index, animated: animated, isJump: true, completion: completion)
Expand Down
4 changes: 2 additions & 2 deletions Sources/Navigator/Decorator/DiffableDecoration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ enum DecorationChange {
}

extension Array where Element == DiffableDecoration {
func changesByHREF(from source: [DiffableDecoration]) -> [String: [DecorationChange]] {
func changesByHREF(from source: [DiffableDecoration]) -> [AnyURL: [DecorationChange]] {
let changeset = StagedChangeset(source: source, target: self)

var changes: [String: [DecorationChange]] = [:]
var changes: [AnyURL: [DecorationChange]] = [:]

func register(_ change: DecorationChange, at locator: Locator) {
var resourceChanges: [DecorationChange] = changes[locator.href] ?? []
Expand Down
4 changes: 2 additions & 2 deletions Sources/Navigator/EPUB/EPUBFixedSpreadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ final class EPUBFixedSpreadView: EPUBSpreadView {
goToCompletions.complete()
}

override func evaluateScript(_ script: String, inHREF href: String?, completion: ((Result<Any, Error>) -> Void)?) {
let href = href ?? ""
override func evaluateScript(_ script: String, inHREF href: AnyURL? = nil, completion: ((Result<Any, any Error>) -> Void)? = nil) {
let href = href?.string ?? ""
let script = "spread.eval('\(href)', `\(script.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "`", with: "\\`"))`);"
super.evaluateScript(script, completion: completion)
}
Expand Down
45 changes: 27 additions & 18 deletions Sources/Navigator/EPUB/EPUBNavigatorViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -431,13 +431,13 @@ open class EPUBNavigatorViewController: UIViewController,
}

/// Mapping between reading order hrefs and the table of contents title.
private lazy var tableOfContentsTitleByHref: [String: String] = {
func fulfill(linkList: [Link]) -> [String: String] {
var result = [String: String]()
private lazy var tableOfContentsTitleByHref: [AnyURL: String] = {
func fulfill(linkList: [Link]) -> [AnyURL: String] {
var result = [AnyURL: String]()

for link in linkList {
if let title = link.title {
result[link.href] = title
result[link.url()] = title
}
let subResult = fulfill(linkList: link.children)
result.merge(subResult) { current, _ -> String in
Expand Down Expand Up @@ -565,7 +565,7 @@ open class EPUBNavigatorViewController: UIViewController,
return nil
}

return readingOrder.firstIndex(withHREF: spreads[currentSpreadIndex].left.href)
return readingOrder.firstIndexWithHREF(spreads[currentSpreadIndex].left.url())
}

private let reloadSpreadsCompletions = CompletionList()
Expand Down Expand Up @@ -615,7 +615,7 @@ open class EPUBNavigatorViewController: UIViewController,
)

let initialIndex: Int = {
if let href = locator?.href, let foundIndex = self.spreads.firstIndex(withHref: href) {
if let href = locator?.href, let foundIndex = self.spreads.firstIndexWithHREF(href) {
return foundIndex
} else {
return 0
Expand All @@ -633,10 +633,10 @@ open class EPUBNavigatorViewController: UIViewController,
}
}

private func loadedSpreadView(forHREF href: String) -> EPUBSpreadView? {
private func loadedSpreadViewForHREF<T: URLConvertible>(_ href: T) -> EPUBSpreadView? {
paginationView.loadedViews
.compactMap { _, view in view as? EPUBSpreadView }
.first { $0.spread.links.first(withHREF: href) != nil }
.first { $0.spread.links.firstWithHREF(href) != nil }
}

// MARK: - Navigator
Expand Down Expand Up @@ -671,21 +671,21 @@ open class EPUBNavigatorViewController: UIViewController,
}

let link = spreadView.focusedResource ?? spreadView.spread.leading
let href = link.href
let href = link.url()
let progression = min(max(spreadView.progression(in: href), 0.0), 1.0)

if
// The positions are not always available, for example a Readium
// WebPub doesn't have any unless a Publication Positions Web
// Service is provided
let index = readingOrder.firstIndex(withHREF: href),
let index = readingOrder.firstIndexWithHREF(href),
let positionList = positionsByReadingOrder.getOrNil(index),
positionList.count > 0
{
// Gets the current locator from the positionList, and fill its missing data.
let positionIndex = Int(ceil(progression * Double(positionList.count - 1)))
return positionList[positionIndex].copy(
title: tableOfContentsTitleByHref[href],
title: tableOfContentsTitleByHref[equivalent: href],
locations: { $0.progression = progression }
)
} else {
Expand Down Expand Up @@ -726,8 +726,10 @@ open class EPUBNavigatorViewController: UIViewController,
}

public func go(to locator: Locator, animated: Bool, completion: @escaping () -> Void) -> Bool {
let locator = publication.normalizeLocator(locator)

guard
let spreadIndex = spreads.firstIndex(withHref: locator.href),
let spreadIndex = spreads.firstIndexWithHREF(locator.href),
on(.jump(locator))
else {
return false
Expand Down Expand Up @@ -796,7 +798,11 @@ open class EPUBNavigatorViewController: UIViewController,

public func apply(decorations: [Decoration], in group: String) {
let source = self.decorations[group] ?? []
let target = decorations.map { DiffableDecoration(decoration: $0) }
let target = decorations.map { d in
var d = d
d.locator = publication.normalizeLocator(d.locator)
return DiffableDecoration(decoration: d)
}

self.decorations[group] = target

Expand All @@ -815,7 +821,7 @@ open class EPUBNavigatorViewController: UIViewController,
guard let script = changes.javascript(forGroup: group, styles: config.decorationTemplates) else {
continue
}
loadedSpreadView(forHREF: href)?.evaluateScript(script, inHREF: href)
loadedSpreadViewForHREF(href)?.evaluateScript(script, inHREF: href)
}
}
}
Expand Down Expand Up @@ -922,7 +928,7 @@ extension EPUBNavigatorViewController: EPUBNavigatorViewModelDelegate {
for (_, view) in paginationView.loadedViews {
guard
let view = view as? EPUBSpreadView,
view.spread.links.first(withHREF: href) != nil
view.spread.links.firstWithHREF(href) != nil
else {
continue
}
Expand Down Expand Up @@ -966,10 +972,10 @@ extension EPUBNavigatorViewController: EPUBSpreadViewDelegate {

spreadView.evaluateScript("(function() {\n\(script)\n})();") { _ in
for link in spreadView.spread.links {
let href = link.href
let href = link.url()
for (group, decorations) in self.decorations {
let decorations = decorations
.filter { $0.decoration.locator.href == href }
.filter { $0.decoration.locator.href.isEquivalentTo(href) }
.map { DecorationChange.add($0.decoration) }

guard let script = decorations.javascript(forGroup: group, styles: self.config.decorationTemplates) else {
Expand Down Expand Up @@ -1018,7 +1024,10 @@ extension EPUBNavigatorViewController: EPUBSpreadViewDelegate {
}

func spreadView(_ spreadView: EPUBSpreadView, didTapOnInternalLink href: String, clickEvent: ClickEvent?) {
guard var link = publication.link(withHREF: href) else {
guard
let url = AnyURL(string: href),
var link = publication.linkWithHREF(url)
else {
log(.warning, "Cannot find link with HREF: \(href)")
return
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/Navigator/EPUB/EPUBNavigatorViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protocol EPUBNavigatorViewModelDelegate: AnyObject {
enum EPUBScriptScope {
case currentResource
case loadedResources
case resource(href: String)
case resource(href: AnyURL)
}

final class EPUBNavigatorViewModel: Loggable {
Expand Down Expand Up @@ -173,8 +173,8 @@ final class EPUBNavigatorViewModel: Loggable {
}
}

func url(to link: Link) -> AnyURL? {
try? link.url(relativeTo: publicationBaseURL)
func url(to link: Link) -> AnyURL {
link.url(relativeTo: publicationBaseURL)
}

private func serveFile(at file: FileURL, baseEndpoint: HTTPServerEndpoint) throws -> HTTPURL {
Expand Down Expand Up @@ -351,7 +351,7 @@ final class EPUBNavigatorViewModel: Loggable {
func injectReadiumCSS(in resource: Resource) -> Resource {
let link = resource.link
guard
link.mediaType.isHTML,
link.mediaType?.isHTML == true,
publication.metadata.presentation.layout(of: link) == .reflowable
else {
return resource
Expand Down
14 changes: 7 additions & 7 deletions Sources/Navigator/EPUB/EPUBReflowableSpreadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,7 @@ final class EPUBReflowableSpreadView: EPUBSpreadView {
return
}
let link = spread.leading
guard let url = viewModel.url(to: link) else {
log(.error, "Can't get URL for link \(link.href)")
return
}
let url = viewModel.url(to: link)
webView.load(URLRequest(url: url.url))
}

Expand Down Expand Up @@ -172,8 +169,11 @@ final class EPUBReflowableSpreadView: EPUBSpreadView {

// MARK: - Location and progression

override func progression(in href: String) -> Double {
guard spread.leading.href == href, let progression = progression else {
override func progression<T>(in href: T) -> Double where T: URLConvertible {
guard
spread.leading.url().isEquivalentTo(href),
let progression = progression
else {
return 0
}
return progression
Expand Down Expand Up @@ -261,7 +261,7 @@ final class EPUBReflowableSpreadView: EPUBSpreadView {
}

private func go(to locator: Locator, completion: @escaping (Bool) -> Void) {
guard ["", "#"].contains(locator.href) || spread.contains(href: locator.href) else {
guard ["", "#"].contains(locator.href.string) || spread.contains(href: locator.href) else {
log(.warning, "The locator's href is not in the spread")
completion(false)
return
Expand Down
Loading