Skip to content

Commit

Permalink
Lazy load background tabs at app startup (#553)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/1177771139624306/1201065943414949/f

Description:
I added a mechanism that records selected tabs in chronological order and reloads them in that
order at app startup. The logic is as follows:

* app launches
* if there are no URL tabs or only 1 URL tab and it's currently selected, do nothing, don't even initialize
* if current tab is a URL tab, wait until it finishes loading (or fails to load), otherwise proceed immediately
* pick up to 3 most recently visited URL tab and reload them in background
* as background tabs finish (or fail) loading, repeat previous step (keeping at most 3 concurrent loads)
  until 20 background tabs are loaded or there are no more URL tabs to load
* as the user switches through tabs during lazy loading, record visited tabs and remove
  them from the lazy loading queue (they were visited manually which triggered a reload)
* if 20 tabs were reloaded in background or there are no more not activated URL tabs, report finished work

Additional details:

* TabLazyLoader is owned by TabCollectionViewModel which serves as its data source
* When lazy loader reports completion, it gets deallocated
* Lazy loader does not differentiate between successful and failed loads, it only records website load 
  attempts and completion (regardless of the outcome) - it's therefore not affected by "no internet" scenario
* the implementation is based on generic types to allow for more thorough unit testing
* the mechanism is inspired by how Chromium-based browsers work, but magic numbers 3 and 20 
  mentioned above were picked by Gabriel - you'll find more information in the Asana task.
* OSLog.tabLazyLoading was added to help debugging lazy loading (disabled by default)
* If there are more than 20 tabs at app startup, lazy loading starts with 10 tabs adjacent
  to the current one, before proceeding to 10 most recently selected.
* Lazy loading is paused every time the user interacts with the current tab (triggers a navigation).
  • Loading branch information
ayoy committed Apr 26, 2022
1 parent db86137 commit a750777
Show file tree
Hide file tree
Showing 13 changed files with 1,053 additions and 4 deletions.
32 changes: 32 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
339A6B5826A044BA00E3DAE8 /* duckduckgo-privacy-dashboard in Resources */ = {isa = PBXBuildFile; fileRef = 339A6B5726A044BA00E3DAE8 /* duckduckgo-privacy-dashboard */; };
371C0A2927E33EDC0070591F /* FeedbackPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */; };
371E141927E92E42009E3B5B /* MultilineScrollableTextFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371E141827E92E42009E3B5B /* MultilineScrollableTextFix.swift */; };
37534C9E28104D9B002621E7 /* TabLazyLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37534C9D28104D9B002621E7 /* TabLazyLoaderTests.swift */; };
37534CA028113101002621E7 /* LazyLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37534C9F28113101002621E7 /* LazyLoadable.swift */; };
37534CA3281132CB002621E7 /* TabLazyLoaderDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37534CA2281132CB002621E7 /* TabLazyLoaderDataSource.swift */; };
37534CA52811987D002621E7 /* AdjacentItemEnumeratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37534CA42811987D002621E7 /* AdjacentItemEnumeratorTests.swift */; };
37534CA8281198CD002621E7 /* AdjacentItemEnumerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37534CA62811988E002621E7 /* AdjacentItemEnumerator.swift */; };
376705AF27EB488600DD8D76 /* RoundedSelectionRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0511B3262CAA5A00F6079C /* RoundedSelectionRowView.swift */; };
376705B327EC7D4F00DD8D76 /* TextButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376705B227EC7D4F00DD8D76 /* TextButton.swift */; };
3776582D27F71652009A6B35 /* WebsiteBreakageReportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776582C27F71652009A6B35 /* WebsiteBreakageReportTests.swift */; };
Expand All @@ -33,6 +38,7 @@
37AFCE8927DA33BA00471A10 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8827DA33BA00471A10 /* Preferences.swift */; };
37AFCE8B27DB69BC00471A10 /* PreferencesDefaultBrowserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8A27DB69BC00471A10 /* PreferencesDefaultBrowserView.swift */; };
37AFCE9227DB8CAD00471A10 /* PreferencesAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE9127DB8CAD00471A10 /* PreferencesAboutView.swift */; };
37B11B3928095E6600CBB621 /* TabLazyLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B11B3828095E6600CBB621 /* TabLazyLoader.swift */; };
37CC53EC27E8A4D10028713D /* PreferencesPrivacyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC53EB27E8A4D10028713D /* PreferencesPrivacyView.swift */; };
37CC53F027E8D1440028713D /* PreferencesDownloadsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC53EF27E8D1440028713D /* PreferencesDownloadsView.swift */; };
37CC53F427E8D4620028713D /* NSPathControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC53F327E8D4620028713D /* NSPathControlView.swift */; };
Expand Down Expand Up @@ -762,6 +768,11 @@
339A6B5726A044BA00E3DAE8 /* duckduckgo-privacy-dashboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "duckduckgo-privacy-dashboard"; path = "Submodules/duckduckgo-privacy-dashboard"; sourceTree = SOURCE_ROOT; };
371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPresenter.swift; sourceTree = "<group>"; };
371E141827E92E42009E3B5B /* MultilineScrollableTextFix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineScrollableTextFix.swift; sourceTree = "<group>"; };
37534C9D28104D9B002621E7 /* TabLazyLoaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabLazyLoaderTests.swift; sourceTree = "<group>"; };
37534C9F28113101002621E7 /* LazyLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyLoadable.swift; sourceTree = "<group>"; };
37534CA2281132CB002621E7 /* TabLazyLoaderDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabLazyLoaderDataSource.swift; sourceTree = "<group>"; };
37534CA42811987D002621E7 /* AdjacentItemEnumeratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdjacentItemEnumeratorTests.swift; sourceTree = "<group>"; };
37534CA62811988E002621E7 /* AdjacentItemEnumerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdjacentItemEnumerator.swift; sourceTree = "<group>"; };
376705B227EC7D4F00DD8D76 /* TextButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextButton.swift; sourceTree = "<group>"; };
3776582C27F71652009A6B35 /* WebsiteBreakageReportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsiteBreakageReportTests.swift; sourceTree = "<group>"; };
3776582E27F82E62009A6B35 /* AutofillPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillPreferences.swift; sourceTree = "<group>"; };
Expand All @@ -774,6 +785,7 @@
37AFCE8827DA33BA00471A10 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
37AFCE8A27DB69BC00471A10 /* PreferencesDefaultBrowserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesDefaultBrowserView.swift; sourceTree = "<group>"; };
37AFCE9127DB8CAD00471A10 /* PreferencesAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesAboutView.swift; sourceTree = "<group>"; };
37B11B3828095E6600CBB621 /* TabLazyLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabLazyLoader.swift; sourceTree = "<group>"; };
37CC53EB27E8A4D10028713D /* PreferencesPrivacyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesPrivacyView.swift; sourceTree = "<group>"; };
37CC53EF27E8D1440028713D /* PreferencesDownloadsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesDownloadsView.swift; sourceTree = "<group>"; };
37CC53F327E8D4620028713D /* NSPathControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPathControlView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1550,6 +1562,16 @@
path = dist;
sourceTree = "<group>";
};
37534CA128113277002621E7 /* TabLazyLoader */ = {
isa = PBXGroup;
children = (
37534C9F28113101002621E7 /* LazyLoadable.swift */,
37534CA2281132CB002621E7 /* TabLazyLoaderDataSource.swift */,
37B11B3828095E6600CBB621 /* TabLazyLoader.swift */,
);
path = TabLazyLoader;
sourceTree = "<group>";
};
3776582B27F7163B009A6B35 /* Website Breakage Report */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1971,6 +1993,7 @@
85C6A29525CC1FFD00EEB5F1 /* UserDefaultsWrapper.swift */,
B6AAAC2C260330580029438D /* PublishedAfter.swift */,
4BB6CE5E26B77ED000EC5860 /* Cryptography.swift */,
37534CA62811988E002621E7 /* AdjacentItemEnumerator.swift */,
);
path = Utilities;
sourceTree = "<group>";
Expand Down Expand Up @@ -2901,6 +2924,7 @@
isa = PBXGroup;
children = (
AA9FF95E24A1FB680039E328 /* TabCollectionViewModel.swift */,
37534CA128113277002621E7 /* TabLazyLoader */,
);
path = ViewModel;
sourceTree = "<group>";
Expand Down Expand Up @@ -3164,6 +3188,8 @@
children = (
AAC9C01D24CB6BEB00AD1325 /* TabCollectionViewModelTests.swift */,
AAE39D1A24F44885008EF28B /* TabCollectionViewModelDelegateMock.swift */,
37534C9D28104D9B002621E7 /* TabLazyLoaderTests.swift */,
37534CA42811987D002621E7 /* AdjacentItemEnumeratorTests.swift */,
);
path = ViewModel;
sourceTree = "<group>";
Expand Down Expand Up @@ -4057,8 +4083,10 @@
AAA0CC572539EBC90079BC96 /* FaviconUserScript.swift in Sources */,
B6A9E45A261460350067D1B9 /* ApiRequestError.swift in Sources */,
AADCBF3A26F7C2CE00EF67A8 /* LottieAnimationCache.swift in Sources */,
37534CA3281132CB002621E7 /* TabLazyLoaderDataSource.swift in Sources */,
4B723E0E26B0006300E14D75 /* LoginImport.swift in Sources */,
85589E9627BFE25D0038AD11 /* FailedAssertionView.swift in Sources */,
37534CA028113101002621E7 /* LazyLoadable.swift in Sources */,
EAE42800275D47FA00DAC26B /* ClickToLoadModel.swift in Sources */,
0230C0A3272080090018F728 /* KeyedCodingExtension.swift in Sources */,
B6C0B23026E61D630031CB7F /* DownloadListStore.swift in Sources */,
Expand Down Expand Up @@ -4116,6 +4144,7 @@
0230C0A52721F3750018F728 /* GPCRequestFactory.swift in Sources */,
9833912F27AAA3CE00DAF119 /* AppTrackerDataSetProvider.swift in Sources */,
4BA1A6B3258B080A00F6F690 /* EncryptionKeyGeneration.swift in Sources */,
37B11B3928095E6600CBB621 /* TabLazyLoader.swift in Sources */,
4B723E0B26B0005B00E14D75 /* CSVImportViewController.swift in Sources */,
8589063C267BCDC000D23B0D /* SaveCredentialsViewController.swift in Sources */,
4BBE0AA727B9B027003B37A8 /* PopUpButton.swift in Sources */,
Expand Down Expand Up @@ -4527,6 +4556,7 @@
37D2771527E870D4003365FD /* PreferencesAppearanceView.swift in Sources */,
AA72D5FE25FFF94E00C77619 /* NSMenuItemExtension.swift in Sources */,
4BA1A6C2258B0A1300F6F690 /* ContiguousBytesExtension.swift in Sources */,
37534CA8281198CD002621E7 /* AdjacentItemEnumerator.swift in Sources */,
AA9B7C7E26A06E040008D425 /* TrackerInfo.swift in Sources */,
B6553692268440D700085A79 /* WKProcessPool+GeolocationProvider.swift in Sources */,
);
Expand All @@ -4543,9 +4573,11 @@
142879DA24CE1179005419BB /* SuggestionViewModelTests.swift in Sources */,
4B9292C12667103100AD2C21 /* BookmarkMigrationTests.swift in Sources */,
4B9292BC2667103100AD2C21 /* BookmarkSidebarTreeControllerTests.swift in Sources */,
37534CA52811987D002621E7 /* AdjacentItemEnumeratorTests.swift in Sources */,
B662D3DC2755DF670035D4D6 /* OldPixelDataModel.xcdatamodeld in Sources */,
B6DA44232616CABC00DD1EC2 /* PixelArgumentsTests.swift in Sources */,
AAEC74BC2642F0F800C2EFBC /* History.xcdatamodeld in Sources */,
37534C9E28104D9B002621E7 /* TabLazyLoaderTests.swift in Sources */,
85F1B0C925EF9759004792B6 /* URLEventHandlerTests.swift in Sources */,
4B9292BD2667103100AD2C21 /* BookmarkOutlineViewDataSourceTests.swift in Sources */,
B6A5A27925B93FFF00AA7ADA /* StateRestorationManagerTests.swift in Sources */,
Expand Down
14 changes: 12 additions & 2 deletions DuckDuckGo/Browser Tab/Model/Tab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protocol TabDelegate: FileDownloadManagerDelegate, ContentOverlayUserScriptDeleg

// swiftlint:disable type_body_length
// swiftlint:disable file_length
final class Tab: NSObject {
final class Tab: NSObject, Identifiable {

enum TabContent: Equatable {
case homePage
Expand Down Expand Up @@ -147,7 +147,9 @@ final class Tab: NSObject {
parentTab: Tab? = nil,
shouldLoadInBackground: Bool = false,
canBeClosedWithBack: Bool = false,
currentDownload: URL? = nil) {
lastSelectedAt: Date? = nil,
currentDownload: URL? = nil
) {

self.content = content
self.faviconManagement = faviconManagement
Expand All @@ -160,6 +162,7 @@ final class Tab: NSObject {
self.parentTab = parentTab
self._canBeClosedWithBack = canBeClosedWithBack
self.sessionStateData = sessionStateData
self.lastSelectedAt = lastSelectedAt
self.currentDownload = currentDownload

let configuration = webViewConfiguration ?? WKWebViewConfiguration()
Expand Down Expand Up @@ -189,6 +192,7 @@ final class Tab: NSObject {
// MARK: - Event Publishers

let webViewDidFinishNavigationPublisher = PassthroughSubject<Void, Never>()
let webViewDidFailNavigationPublisher = PassthroughSubject<Void, Never>()

@MainActor
@Published var isAMPProtectionExtracting: Bool = false
Expand All @@ -205,6 +209,8 @@ final class Tab: NSObject {

var fbBlockingEnabled = true

var isLazyLoadingInProgress = false

@Published private(set) var content: TabContent {
didSet {
handleFavicon(oldContent: oldValue)
Expand Down Expand Up @@ -234,6 +240,8 @@ final class Tab: NSObject {
self.content = content
}
}

var lastSelectedAt: Date?

@Published var title: String?
@Published var error: Error?
Expand Down Expand Up @@ -1100,6 +1108,7 @@ extension Tab: WKNavigationDelegate {
// https://app.asana.com/0/1199230911884351/1200381133504356/f
// hasError = true

webViewDidFailNavigationPublisher.send()
invalidateSessionStateData()
}

Expand All @@ -1113,6 +1122,7 @@ extension Tab: WKNavigationDelegate {
}

self.error = error
webViewDidFailNavigationPublisher.send()
}

@available(macOS 11.3, *)
Expand Down
20 changes: 18 additions & 2 deletions DuckDuckGo/Browser Tab/ViewModel/TabViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,27 @@ final class TabViewModel {
}

private func subscribeToTitle() {
tab.$title.receive(on: DispatchQueue.main).sink { [weak self] _ in self?.updateTitle() } .store(in: &cancellables)
tab.$title
.filter { [weak self] _ in
self?.tab.isLazyLoadingInProgress == false
}
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.updateTitle()
}
.store(in: &cancellables)
}

private func subscribeToFavicon() {
tab.$favicon.receive(on: DispatchQueue.main).sink { [weak self] _ in self?.updateFavicon() } .store(in: &cancellables)
tab.$favicon
.filter { [weak self] _ in
self?.tab.isLazyLoadingInProgress == false
}
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.updateFavicon()
}
.store(in: &cancellables)
}

private func subscribeToTabError() {
Expand Down
112 changes: 112 additions & 0 deletions DuckDuckGo/Common/Utilities/AdjacentItemEnumerator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// AdjacentItemEnumerator.swift
//
// Copyright © 2022 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

/**
* This struct generates array indices adjacent to a given index.
*
* The _adjacent_ indices are generated as diffs against given index
* with the following pattern:
*
* 1, -1, 2, -2, 3, -3, 4, -4, ...
*
*/
struct AdjacentItemEnumerator {

/**
* The index of an item for which adjacent items indices are computed.
*/
var itemIndex: Int

/**
* Last valid adjacent item index returned by `nextIndex(arraySize:)`.
*/
var currentAdjacentIndex: Int {
itemIndex + currentDiff
}

/**
* Computes next adjacent index, constrained by `arraySize`.
*
* Returns the next available and valid index, between by `0` and `arraySize-1`,
* or `nil` if the index falls outside array bounds.
*/
mutating func nextIndex(arraySize: Int) -> Int? {
if currentDiff != 0 {
operation = operation.next
}

previousDiff = currentDiff
currentDiff = operation.perform(with: currentDiff)

let newIndex = itemIndex + currentDiff

if newIndex < 0 || newIndex >= arraySize {
let previousIndex = itemIndex + previousDiff
if previousIndex <= 0 || previousIndex >= arraySize - 1 {
return nil
}
return nextIndex(arraySize: arraySize)
}
return newIndex
}

/**
* Resets the enumerator internal state
*
* Calling this function is equivalent to reinstantiating the enumerator.
*/
mutating func reset() {
currentDiff = 0
previousDiff = 0
operation = .toggleSignAndAdvance
}

init(itemIndex: Int = 0) {
self.itemIndex = itemIndex
}

// MARK: - Private

private var currentDiff = 0
private var previousDiff = 0
private var operation: Operation = .toggleSignAndAdvance

private enum Operation {
case toggleSign, toggleSignAndAdvance

func perform(with value: Int) -> Int {
switch self {
case .toggleSign:
return value * -1
case .toggleSignAndAdvance:
return (value * -1) + 1
}
}

var next: Operation {
switch self {
case .toggleSign:
return .toggleSignAndAdvance
case .toggleSignAndAdvance:
return .toggleSign
}
}
}
}
6 changes: 6 additions & 0 deletions DuckDuckGo/Common/Utilities/Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ extension OSLog {
Logging.autoLockLoggingEnabled ? Logging.autoLockLog : .disabled
}

static var tabLazyLoading: OSLog {
Logging.tabLazyLoaderLoggingEnabled ? Logging.tabLazyLoaderLog : .disabled
}
}

struct Logging {
Expand Down Expand Up @@ -92,6 +95,9 @@ struct Logging {
fileprivate static let autoLockLoggingEnabled = false
fileprivate static let autoLockLog: OSLog = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "DuckDuckGo", category: "Auto-Lock")

fileprivate static let tabLazyLoaderLoggingEnabled = false
fileprivate static let tabLazyLoaderLog: OSLog = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "DuckDuckGo", category: "Lazy Loading")

fileprivate static let autoconsentLoggingEnabled = false
fileprivate static let autoconsentLog: OSLog = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "DuckDuckGo", category: "Autoconsent")

Expand Down
3 changes: 3 additions & 0 deletions DuckDuckGo/State Restoration/Tab+NSSecureCoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extension Tab: NSSecureCoding {
static let tabType = "tabType"
static let preferencePane = "preferencePane"
static let visitedDomains = "visitedDomains"
static let lastSelectedAt = "lastSelectedAt"
static let currentDownload = "currentDownload"
}

Expand All @@ -52,6 +53,7 @@ extension Tab: NSSecureCoding {
title: decoder.decodeIfPresent(at: NSSecureCodingKeys.title),
favicon: decoder.decodeIfPresent(at: NSSecureCodingKeys.favicon),
sessionStateData: decoder.decodeIfPresent(at: NSSecureCodingKeys.sessionStateData),
lastSelectedAt: decoder.decodeIfPresent(at: NSSecureCodingKeys.lastSelectedAt),
currentDownload: currentDownload)
}

Expand All @@ -64,6 +66,7 @@ extension Tab: NSSecureCoding {
favicon.map(coder.encode(forKey: NSSecureCodingKeys.favicon))
getActualSessionStateData().map(coder.encode(forKey: NSSecureCodingKeys.sessionStateData))
coder.encode(content.type.rawValue, forKey: NSSecureCodingKeys.tabType)
lastSelectedAt.map(coder.encode(forKey: NSSecureCodingKeys.lastSelectedAt))
coder.encode(currentDownload, forKey: NSSecureCodingKeys.currentDownload)

if let pane = content.preferencePane {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,20 @@ extension WindowsManager {
let isOriginalKeyWindowPresent = Self.windows.contains(where: {$0.isKeyWindow})

var newKeyWindow: NSWindow?
var newKeyWindowModel: TabCollectionViewModel?
for (idx, item) in state.windows.enumerated() {
guard let window = self.openNewWindow(with: item.model, showWindow: false) else { continue }
window.setContentSize(item.frame.size)
window.setFrameOrigin(item.frame.origin)

if idx == state.keyWindowIndex {
newKeyWindow = window
newKeyWindowModel = item.model
}
}
if !isOriginalKeyWindowPresent {
newKeyWindow?.makeKeyAndOrderFront(self)
newKeyWindowModel?.setUpLazyLoadingIfNeeded()
}

if !state.windows.isEmpty {
Expand Down
Loading

0 comments on commit a750777

Please sign in to comment.