Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
Fixes #1144
Browse files Browse the repository at this point in the history
Fixed a background thread issue where "displayTitle" is being called on WKWebView on a non-main thread causing the PrintPreview to have undefined-behaviour.
Fixed opening Authenticated documents in Private-Browsing mode when it opens in a new tab.
Fixed opening Authenticated documents and attempting to share, or print them.
Fixes tabs not having the same data store as other tabs (IE: Log-In to any website on one tab should log you in on another tab).
Fixed destroying of data-store to only happen when ALL tabs are destroyed.
Changed Temporary Storage to pull out the document from the WebPage instead of making server calls outside the webview's session (IE: No need for cookie injecting, etc).. Instead, we get the webview to give us the document via Javascript injection which passes the document to iOS for display/saving, etc..
  • Loading branch information
Brandon-T committed Jul 15, 2019
1 parent 384f76a commit e721c44
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 20 deletions.
8 changes: 8 additions & 0 deletions Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@
5DE768AE20B443E500FF5533 /* JSONSerializationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE768AD20B443E500FF5533 /* JSONSerializationExtensions.swift */; };
5DE768B020B4601700FF5533 /* UIColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE768AF20B4601600FF5533 /* UIColorExtensions.swift */; };
5DE768B120B4713000FF5533 /* Storage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FCAE21A1ABB51F800877008 /* Storage.framework */; };
5E3477E922D7771700B0D5F8 /* ResourceDownloader.js in Resources */ = {isa = PBXBuildFile; fileRef = 5E3477E822D7771700B0D5F8 /* ResourceDownloader.js */; };
5E34781022D7A1D200B0D5F8 /* ResourceDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E34780F22D7A1D200B0D5F8 /* ResourceDownloadManager.swift */; };
744B0FFE1B4F172E00100422 /* ToolbarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 744B0FFD1B4F172E00100422 /* ToolbarTests.swift */; };
744ED5611DBFEB8D00A2B5BE /* MailtoLinkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 744ED5601DBFEB8D00A2B5BE /* MailtoLinkHandler.swift */; };
7479B4EF1C5306A200DF000B /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7479B4ED1C5306A200DF000B /* Reachability.swift */; };
Expand Down Expand Up @@ -1861,6 +1863,8 @@
5DE768AA20B346B700FF5533 /* BraveGlobalShieldStats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BraveGlobalShieldStats.swift; sourceTree = "<group>"; };
5DE768AD20B443E500FF5533 /* JSONSerializationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONSerializationExtensions.swift; sourceTree = "<group>"; };
5DE768AF20B4601600FF5533 /* UIColorExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColorExtensions.swift; sourceTree = "<group>"; };
5E3477E822D7771700B0D5F8 /* ResourceDownloader.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = ResourceDownloader.js; sourceTree = "<group>"; };
5E34780F22D7A1D200B0D5F8 /* ResourceDownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourceDownloadManager.swift; sourceTree = "<group>"; };
744B0FFD1B4F172E00100422 /* ToolbarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolbarTests.swift; sourceTree = "<group>"; };
744ED5601DBFEB8D00A2B5BE /* MailtoLinkHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MailtoLinkHandler.swift; sourceTree = "<group>"; };
7479B4ED1C5306A200DF000B /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Reachability.swift; path = ThirdParty/Reachability.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3583,6 +3587,7 @@
595E0EDA21CAD97C00813D49 /* CookieControl.js */,
F930CDAB227000F200A23FE1 /* U2F.js */,
F99505FE22937E3900CC6543 /* U2F-low-level.js */,
5E3477E822D7771700B0D5F8 /* ResourceDownloader.js */,
);
path = UserScripts;
sourceTree = "<group>";
Expand Down Expand Up @@ -3818,6 +3823,7 @@
D3C744CC1A687D6C004CE85D /* URIFixup.swift */,
D0FCF7F41FE45842004A7995 /* UserScriptManager.swift */,
279C756A219DDE3B001CD1CB /* FingerprintingProtection.swift */,
5E34780F22D7A1D200B0D5F8 /* ResourceDownloadManager.swift */,
);
indentWidth = 4;
path = Browser;
Expand Down Expand Up @@ -5179,6 +5185,7 @@
E4B7B77D1A793CF20022C5E0 /* FiraSans-Regular.ttf in Resources */,
4422D55221BFFB7E00BF1855 /* make_unicode_casefold.py in Resources */,
E4B7B7791A793CF20022C5E0 /* FiraSans-Light.ttf in Resources */,
5E3477E922D7771700B0D5F8 /* ResourceDownloader.js in Resources */,
F99505FF22937E3900CC6543 /* U2F-low-level.js in Resources */,
D0FCF8081FE4772D004A7995 /* MainFrameAtDocumentStart.js in Resources */,
0A0D3D5021A5609600BEE65B /* SafeBrowsingError.html in Resources */,
Expand Down Expand Up @@ -5776,6 +5783,7 @@
D3B6923D1B9F9444004B87A4 /* FindInPageBar.swift in Sources */,
2F44FC721A9E840300FD20CC /* SettingsNavigationController.swift in Sources */,
0AADC4D220D2A6A200FDE368 /* FavoritesHelper.swift in Sources */,
5E34781022D7A1D200B0D5F8 /* ResourceDownloadManager.swift in Sources */,
C690C2142130121E00E6EEE9 /* WebImageCacheManager.swift in Sources */,
A104E199210A384400D2323E /* ShieldsViewController.swift in Sources */,
D3BA7E0E1B0E934F00153782 /* ContextMenuHelper.swift in Sources */,
Expand Down
14 changes: 13 additions & 1 deletion Client/Frontend/Browser/BraveWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,22 @@ import WebKit

class BraveWebView: WKWebView {

private static var nonPersistentDataStore: WKWebsiteDataStore?
private static func sharedNonPersistentDataStore() -> WKWebsiteDataStore {
if let dataStore = nonPersistentDataStore {
return dataStore
}

let dataStore = WKWebsiteDataStore.nonPersistent()
nonPersistentDataStore = dataStore
return dataStore
}

init(frame: CGRect, configuration: WKWebViewConfiguration = WKWebViewConfiguration(), isPrivate: Bool = true) {
if isPrivate {
configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent()
configuration.websiteDataStore = BraveWebView.sharedNonPersistentDataStore()
} else {
BraveWebView.nonPersistentDataStore = nil //switching to normal mode destroys all data-stores
configuration.websiteDataStore = WKWebsiteDataStore.default()
}

Expand Down
2 changes: 2 additions & 0 deletions Client/Frontend/Browser/BrowserViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,8 @@ extension BrowserViewController: TabDelegate {
tab.addContentScript(FingerprintingProtection(tab: tab), name: FingerprintingProtection.name())

tab.addContentScript(U2FExtensions(tab: tab), name: U2FExtensions.name())

tab.addContentScript(ResourceDownloadManager(tab: tab), name: ResourceDownloadManager.name())
}

func tab(_ tab: Tab, willDeleteWebView webView: WKWebView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ extension BrowserViewController: WKNavigationDelegate {
// shared to external applications later. Otherwise, clear the old temporary document.
if let tab = tabManager[webView] {
if response.mimeType?.isKindOfHTML == false, let request = request {
tab.temporaryDocument = TemporaryDocument(preflightResponse: response, request: request)
tab.temporaryDocument = TemporaryDocument(preflightResponse: response, request: request, tab: tab)
} else {
tab.temporaryDocument = nil
}
Expand Down
65 changes: 65 additions & 0 deletions Client/Frontend/Browser/ResourceDownloadManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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 WebKit
import Data
import BraveShared

struct DownloadedResourceResponse: Decodable {
let statusCode: Int
let data: Data?

static func from(message: WKScriptMessage) throws -> DownloadedResourceResponse? {
let data = try JSONSerialization.data(withJSONObject: message.body, options: .prettyPrinted)
return try JSONDecoder().decode(DownloadedResourceResponse.self, from: data)
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.statusCode = try container.decode(Int.self, forKey: .statusCode)
self.data = Data(base64Encoded: try container.decode(String.self, forKey: .base64Data))
}

private enum CodingKeys: String, CodingKey {
case statusCode
case base64Data
}
}

class ResourceDownloadManager: TabContentScript {
fileprivate weak var tab: Tab?

init(tab: Tab) {
self.tab = tab
}

static func name() -> String {
return "ResourceDownloadManager"
}

func scriptMessageHandlerName() -> String? {
return "resourceDownloadManager"
}

func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {

do {
let response = try DownloadedResourceResponse.from(message: message)
tab?.temporaryDocument?.onDocumentDownloaded(document: response, error: nil)
} catch let error {
tab?.temporaryDocument?.onDocumentDownloaded(document: nil, error: error)
}
}

static func downloadResource(for tab: Tab, url: URL) {
let token = UserScriptManager.securityToken.uuidString.replacingOccurrences(of: "-", with: "", options: .literal)

tab.webView?.evaluateJavaScript("D\(token).download(\"\(url)\");", completionHandler: { _, error in
if let error = error {
tab.temporaryDocument?.onDocumentDownloaded(document: nil, error: error)
}
})
}
}
6 changes: 5 additions & 1 deletion Client/Frontend/Browser/TabManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,11 @@ class TabManager: NSObject {
}

if tab.isPrivate {
removeAllBrowsingDataForTab(tab)
// Only when ALL tabs are dead, we clean up.
// This is because other tabs share the same data-store.
if allTabs.filter({ $0.isPrivate }).count <= 1 {
removeAllBrowsingDataForTab(tab)
}
}

let oldSelectedTab = selectedTab
Expand Down
8 changes: 6 additions & 2 deletions Client/Frontend/Browser/TabPrintPageRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ private struct PrintedPageUX {
}

class TabPrintPageRenderer: UIPrintPageRenderer {
fileprivate weak var tab: Tab?
private weak var tab: Tab?
private let displayTitle: String

let textAttributes = [NSAttributedString.Key.font: PrintedPageUX.PageTextFont]
let dateString: String

Expand All @@ -20,6 +22,8 @@ class TabPrintPageRenderer: UIPrintPageRenderer {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short

self.displayTitle = tab.displayTitle
self.dateString = dateFormatter.string(from: Date())

super.init()
Expand Down Expand Up @@ -51,7 +55,7 @@ class TabPrintPageRenderer: UIPrintPageRenderer {
let headerRect = paperRect.inset(by: headerInsets)

// page title on left
self.drawTextAtPoint(tab!.displayTitle, rect: headerRect, onLeft: true)
self.drawTextAtPoint(displayTitle, rect: headerRect, onLeft: true)

// date on right
self.drawTextAtPoint(dateString, rect: headerRect, onLeft: false)
Expand Down
65 changes: 50 additions & 15 deletions Client/Frontend/Browser/TemporaryDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,19 @@ import Shared
private let temporaryDocumentOperationQueue = OperationQueue()

class TemporaryDocument: NSObject {
fileprivate let request: URLRequest
fileprivate let filename: String
private weak var tab: Tab?
private let request: URLRequest
private let filename: String

fileprivate var session: URLSession?
private var localFileURL: URL?
private var pendingResult: Deferred<URL>?

fileprivate var downloadTask: URLSessionDownloadTask?
fileprivate var localFileURL: URL?
fileprivate var pendingResult: Deferred<URL>?

init(preflightResponse: URLResponse, request: URLRequest) {
init(preflightResponse: URLResponse, request: URLRequest, tab: Tab) {
self.request = request
self.filename = preflightResponse.suggestedFilename ?? "unknown"
self.tab = tab

super.init()

self.session = URLSession(configuration: .default, delegate: self, delegateQueue: temporaryDocumentOperationQueue)
}

deinit {
Expand All @@ -47,14 +44,52 @@ class TemporaryDocument: NSObject {

let result = Deferred<URL>()
pendingResult = result

downloadTask = session?.downloadTask(with: request)
downloadTask?.resume()

UIApplication.shared.isNetworkActivityIndicatorVisible = true

if let tab = self.tab, let url = request.url {
ResourceDownloadManager.downloadResource(for: tab, url: url)

ensureMainThread {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
} else {
onDocumentDownloaded(document: nil, error: nil)
}

return result
}

func onDocumentDownloaded(document: DownloadedResourceResponse?, error: Error?) {
ensureMainThread {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}

// Store the blob/data in a local temporary file.
if let document = document, let data = document.data, !data.isEmpty {
let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("TempDocs")
let url = tempDirectory.appendingPathComponent(filename)

do {
try FileManager.default.createDirectory(at: tempDirectory, withIntermediateDirectories: true, attributes: nil)
try FileManager.default.removeItem(at: url)
try data.write(to: url, options: [.atomic])

localFileURL = url
pendingResult?.fill(url)
pendingResult = nil
return
} catch {
// let the error pass through to the below handler..
}
}

// If we encounter an error downloading the temp file, just return with the
// original remote URL so it can still be shared as a web URL.
if let url = request.url {
pendingResult?.fill(url)
}

pendingResult = nil
}
}

extension TemporaryDocument: URLSessionTaskDelegate, URLSessionDownloadDelegate {
Expand Down
16 changes: 16 additions & 0 deletions Client/Frontend/Browser/UserScriptManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ class UserScriptManager {

return WKUserScript(source: alteredSource, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
}()

private let resourceDownloadManagerUserScript: WKUserScript? = {
guard let path = Bundle.main.path(forResource: "ResourceDownloader", ofType: "js"), let source = try? String(contentsOfFile: path) else {
log.error("Failed to load ResourceDownloader.js")
return nil
}
var alteredSource: String = source
let token = UserScriptManager.securityToken.uuidString.replacingOccurrences(of: "-", with: "", options: .literal)
alteredSource = alteredSource.replacingOccurrences(of: "$<downloadManager>", with: "D\(token)", options: .literal)

return WKUserScript(source: alteredSource, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
}()

private func reloadUserScripts() {
tab?.webView?.configuration.userContentController.do {
Expand All @@ -139,6 +151,10 @@ class UserScriptManager {
if isU2FEnabled, let script = U2FLowLevelUserScript {
$0.addUserScript(script)
}

if let script = resourceDownloadManagerUserScript {
$0.addUserScript(script)
}
}
}
}
44 changes: 44 additions & 0 deletions Client/Frontend/UserContent/UserScripts/ResourceDownloader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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/.

"use strict";

var $<downloadManager> = (function() {
function postMessage(msg) {
if (msg) {
webkit.messageHandlers.resourceDownloadManager.postMessage(msg);
}
}

function downloadPageResource(link) {
var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.onreadystatechange = function() {
if (this.readyState == XMLHttpRequest.DONE) {
if (this.status == 200) {
var byteArray = new Uint8Array(this.response);
var binaryString = new Array(byteArray.length);

for (var i = 0; i < byteArray.length; ++i) {
binaryString[i] = String.fromCharCode(byteArray[i]);
}

var data = binaryString.join('');
var base64 = window.btoa(data);

postMessage({ "statusCode": this.status, "base64Data": base64 });
}
else {
postMessage({ "statusCode": this.status, "base64Data": "" });
}
}
};
xhr.open("GET", link, true);
xhr.send(null);
};

return {
download: downloadPageResource
};
})()

0 comments on commit e721c44

Please sign in to comment.