<commit_before>/* 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 SDWebImage
import Deferred

class FaviconHandler {
    static let MaximumFaviconSize = 1 * 1024 * 1024 // 1 MiB file size limit

    private var tabObservers: TabObservers!
    private let backgroundQueue = OperationQueue()

    init() {
        self.tabObservers = registerFor(.didLoadPageMetadata, queue: backgroundQueue)
    }

    deinit {
        unregister(tabObservers)
    }

    func loadFaviconURL(_ faviconURL: String, forTab tab: Tab) -> Deferred<Maybe<(Favicon, Data?)>> {
        guard let iconURL = URL(string: faviconURL), let currentURL = tab.url else {
            return deferMaybe(FaviconError())
        }

        let deferred = Deferred<Maybe<(Favicon, Data?)>>()
        let manager = SDWebImageManager.shared()
        let url = currentURL.absoluteString
        let site = Site(url: url, title: "")
        let options: SDWebImageOptions = tab.isPrivate ? SDWebImageOptions([.lowPriority, .cacheMemoryOnly]) : SDWebImageOptions([.lowPriority])

        var fetch: SDWebImageOperation? = nil

        let onProgress: SDWebImageDownloaderProgressBlock = { (receivedSize, expectedSize, _) -> Void in
            if receivedSize > FaviconHandler.MaximumFaviconSize || expectedSize > FaviconHandler.MaximumFaviconSize {
                fetch?.cancel()
            }
        }

        let onCompleted: SDInternalCompletionBlock = { (img, data, _, _, _, url) -> Void in
            guard let img = img, let urlString = url?.absoluteString else {
                deferred.fill(Maybe(failure: FaviconError()))
                return
            }

            let favicon = Favicon(url: urlString, date: Date())
            favicon.width = Int(img.size.width)
            favicon.height = Int(img.size.height)

            tab.favicons.append(favicon)

            if !tab.isPrivate, let appDelegate = UIApplication.shared.delegate as? AppDelegate, let profile = appDelegate.profile {
                profile.favicons.addFavicon(favicon, forSite: site).upon { _ in
                    deferred.fill(Maybe(success: (favicon, data)))
                }
            } else {
                deferred.fill(Maybe(success: (favicon, data)))
            }
        }

        fetch = manager.loadImage(with: iconURL, options: options, progress: onProgress, completed: onCompleted)
        return deferred
    }
}

extension FaviconHandler: TabEventHandler {<commit_msg>Bug 1430753 - [Regression] Pin to top Sites does not work (#3584)<commit_after>/* 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 SDWebImage
import Deferred

class FaviconHandler {
    static let MaximumFaviconSize = 1 * 1024 * 1024 // 1 MiB file size limit

    private var tabObservers: TabObservers!
    private let backgroundQueue = OperationQueue()

    init() {
        self.tabObservers = registerFor(.didLoadPageMetadata, queue: backgroundQueue)
    }

    deinit {
        unregister(tabObservers)
    }

    func loadFaviconURL(_ faviconURL: String, forTab tab: Tab) -> Deferred<Maybe<(Favicon, Data?)>> {
        guard let iconURL = URL(string: faviconURL), let currentURL = tab.url else {
            return deferMaybe(FaviconError())
        }

        let deferred = Deferred<Maybe<(Favicon, Data?)>>()
        let manager = SDWebImageManager.shared()
        let url = currentURL.absoluteString
        let site = Site(url: url, title: "")
        let options: SDWebImageOptions = tab.isPrivate ? SDWebImageOptions([.lowPriority, .cacheMemoryOnly]) : SDWebImageOptions([.lowPriority])

        var fetch: SDWebImageOperation? = nil

        let onProgress: SDWebImageDownloaderProgressBlock = { (receivedSize, expectedSize, _) -> Void in
            if receivedSize > FaviconHandler.MaximumFaviconSize || expectedSize > FaviconHandler.MaximumFaviconSize {
                fetch?.cancel()
            }
        }

        let onSuccess: (Favicon, Data?) -> Void = { (favicon, data) -> Void in
            tab.favicons.append(favicon)

            if !tab.isPrivate, let appDelegate = UIApplication.shared.delegate as? AppDelegate, let profile = appDelegate.profile {
                profile.favicons.addFavicon(favicon, forSite: site).upon { _ in
                    deferred.fill(Maybe(success: (favicon, data)))
                }
            } else {
                deferred.fill(Maybe(success: (favicon, data)))
            }
        }

        let onCompletedSiteFavicon: SDInternalCompletionBlock = { (img, data, _, _, _, url) -> Void in
            guard let img = img, let urlString = url?.absoluteString else {
                deferred.fill(Maybe(failure: FaviconError()))
                return
            }

            let favicon = Favicon(url: urlString, date: Date())
            favicon.width = Int(img.size.width)
            favicon.height = Int(img.size.height)

            onSuccess(favicon, data)
        }

        let onCompletedPageFavicon: SDInternalCompletionBlock = { (img, data, _, _, _, url) -> Void in
            guard let img = img, let urlString = url?.absoluteString else {
                // If we failed to download a page-level icon, try getting the domain-level icon
                // instead before ultimately failing.
                let siteIconURL = currentURL.domainURL.appendingPathComponent("favicon.ico")
                if siteIconURL == url {
                    deferred.fill(Maybe(failure: FaviconError()))
                } else {
                    fetch = manager.loadImage(with: siteIconURL, options: options, progress: onProgress, completed: onCompletedSiteFavicon)
                }

                return
            }

            let favicon = Favicon(url: urlString, date: Date())
            favicon.width = Int(img.size.width)
            favicon.height = Int(img.size.height)

            onSuccess(favicon, data)
        }

        fetch = manager.loadImage(with: iconURL, options: options, progress: onProgress, completed: onCompletedPageFavicon)
        return deferred
    }
}

extension FaviconHandler: TabEventHandler {
{'fraction_non_alphanumeric': 0.09491420621425259, 'fraction_numerical': 0.0060287525119802135, 'mean_word_length': 2.3750652060511217, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 24, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2263235', 'n_tokens_mistral': 1855, 'n_tokens_neox': 1730, 'n_words': 479}