Changeset 282711 in webkit
- Timestamp:
- Sep 17, 2021, 4:46:14 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 1 deleted
- 8 edited
-
Source/WebKit/ChangeLog (modified) (1 diff)
-
Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp (modified) (2 diffs)
-
Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h (modified) (2 diffs)
-
Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp (deleted)
-
Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp (modified) (2 diffs)
-
Source/WebKit/Sources.txt (modified) (1 diff)
-
Source/WebKit/WebKit.xcodeproj/project.pbxproj (modified) (4 diffs)
-
Tools/ChangeLog (modified) (1 diff)
-
Tools/TestWebKitAPI/Tests/WebKitCocoa/ResourceLoadStatistics.mm (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit/ChangeLog
r282705 r282711 1 2021-09-17 Kate Cheney <katherine_cheney@apple.com> 2 3 Remove unnecessary ITP memory store code 4 https://bugs.webkit.org/show_bug.cgi?id=229512 5 <rdar://problem/82644309> 6 7 Reviewed by John Wilander. 8 9 No new tests. Confirmed by existing tests. 10 11 Remove ITP Memory store. This is the first part of a two part operation, 12 which removes the ResourceLoadStatisticsMemoryStore class. The next 13 step will be reducing ResourceLoadStatisticsStore and 14 ResourceLoadStatisticsDatabaseStore to one single ResourceLoadStatisticsStore class. 15 16 * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp: 17 (WebKit::ResourceLoadStatisticsDatabaseStore::populateFromMemoryStore): Deleted. 18 * NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h: 19 * NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp: Removed. 20 * NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp: 21 (WebKit::WebResourceLoadStatisticsStore::WebResourceLoadStatisticsStore): 22 * Sources.txt: 23 * WebKit.xcodeproj/project.pbxproj: 24 1 25 2021-09-17 Chris Dumez <cdumez@apple.com> 2 26 -
trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp
r282429 r282711 35 35 #include "PrivateClickMeasurementManager.h" 36 36 #include "PrivateClickMeasurementManagerProxy.h" 37 #include "ResourceLoadStatisticsMemoryStore.h"38 37 #include "StorageAccessStatus.h" 39 38 #include "WebProcessProxy.h" … … 925 924 } 926 925 927 void ResourceLoadStatisticsDatabaseStore::populateFromMemoryStore(const ResourceLoadStatisticsMemoryStore& memoryStore)928 {929 ASSERT(!RunLoop::isMain());930 931 if (!isEmpty())932 return;933 934 auto transactionScope = beginTransactionIfNecessary();935 936 auto& statisticsMap = memoryStore.data();937 for (const auto& statistic : statisticsMap.values()) {938 auto result = insertObservedDomain(statistic);939 if (!result) {940 ITP_RELEASE_LOG_ERROR(m_sessionID, "%p - ResourceLoadStatisticsDatabaseStore::populateFromMemoryStore insertObservedDomain failed to complete, error message: %" PRIVATE_LOG_STRING, this, m_database.lastErrorMsg());941 ASSERT_NOT_REACHED();942 return;943 }944 }945 946 // Make a separate pass for inter-domain relationships so we947 // can refer to the ObservedDomain table entries948 for (auto& statistic : statisticsMap.values())949 insertDomainRelationships(statistic);950 }951 952 926 void ResourceLoadStatisticsDatabaseStore::merge(WebCore::SQLiteStatement* current, const ResourceLoadStatistics& other) 953 927 { -
trunk/Source/WebKit/NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.h
r282429 r282711 49 49 static constexpr std::array<unsigned, numberOfBucketsPerStatistic> bucketSizes {{ 1, 3, 10, 50, 100 }}; 50 50 51 class ResourceLoadStatisticsMemoryStore;52 53 51 typedef std::pair<String, std::optional<String>> TableAndIndexPair; 54 52 … … 59 57 ~ResourceLoadStatisticsDatabaseStore(); 60 58 61 void populateFromMemoryStore(const ResourceLoadStatisticsMemoryStore&);62 59 void mergeStatistics(Vector<ResourceLoadStatistics>&&) override; 63 60 void clear(CompletionHandler<void()>&&) override; -
trunk/Source/WebKit/NetworkProcess/Classifier/WebResourceLoadStatisticsStore.cpp
r282429 r282711 36 36 #include "PrivateClickMeasurementManager.h" 37 37 #include "ResourceLoadStatisticsDatabaseStore.h" 38 #include "ResourceLoadStatisticsMemoryStore.h"39 38 #include "ShouldGrandfatherStatistics.h" 40 39 #include "StorageAccessStatus.h" … … 167 166 m_statisticsStore = makeUnique<ResourceLoadStatisticsDatabaseStore>(*this, m_statisticsQueue, shouldIncludeLocalhost, resourceLoadStatisticsDirectory, sessionID); 168 167 169 auto memoryStore = makeUnique<ResourceLoadStatisticsMemoryStore>(*this, m_statisticsQueue, shouldIncludeLocalhost);170 downcast<ResourceLoadStatisticsDatabaseStore>(*m_statisticsStore.get()).populateFromMemoryStore(*memoryStore);171 172 168 auto legacyPlistFilePath = FileSystem::pathByAppendingComponent(resourceLoadStatisticsDirectory, "full_browsing_session_resourceLog.plist"); 173 169 if (FileSystem::fileExists(legacyPlistFilePath)) -
trunk/Source/WebKit/Sources.txt
r282230 r282711 84 84 85 85 NetworkProcess/Classifier/ResourceLoadStatisticsDatabaseStore.cpp 86 NetworkProcess/Classifier/ResourceLoadStatisticsMemoryStore.cpp87 86 NetworkProcess/Classifier/ResourceLoadStatisticsStore.cpp 88 87 -
trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj
r282446 r282711 1349 1349 7AF236211E79A40800438A05 /* WebErrors.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AF2361F1E79A3D800438A05 /* WebErrors.h */; }; 1350 1350 7AFBD36321E50F39005DBACB /* WebResourceLoadStatisticsStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AFBD36221E50F39005DBACB /* WebResourceLoadStatisticsStore.h */; }; 1351 7AFBD36721E51BAB005DBACB /* ResourceLoadStatisticsMemoryStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AFBD36521E51BAB005DBACB /* ResourceLoadStatisticsMemoryStore.h */; };1352 1351 7AFBD36F21E546F8005DBACB /* PersistencyUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AFBD36D21E546E3005DBACB /* PersistencyUtils.h */; }; 1353 1352 7B1DB26625668CE1000E26BC /* ArrayReference.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B1DB26525668CE0000E26BC /* ArrayReference.h */; }; … … 4695 4694 7AF236221E79A43100438A05 /* WebErrorsCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebErrorsCocoa.mm; sourceTree = "<group>"; }; 4696 4695 7AFBD36221E50F39005DBACB /* WebResourceLoadStatisticsStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebResourceLoadStatisticsStore.h; path = Classifier/WebResourceLoadStatisticsStore.h; sourceTree = "<group>"; }; 4697 7AFBD36421E51BAB005DBACB /* ResourceLoadStatisticsMemoryStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ResourceLoadStatisticsMemoryStore.cpp; path = Classifier/ResourceLoadStatisticsMemoryStore.cpp; sourceTree = "<group>"; };4698 7AFBD36521E51BAB005DBACB /* ResourceLoadStatisticsMemoryStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResourceLoadStatisticsMemoryStore.h; path = Classifier/ResourceLoadStatisticsMemoryStore.h; sourceTree = "<group>"; };4699 4696 7AFBD36D21E546E3005DBACB /* PersistencyUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersistencyUtils.h; sourceTree = "<group>"; }; 4700 4697 7AFBD36E21E546E3005DBACB /* PersistencyUtils.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PersistencyUtils.cpp; sourceTree = "<group>"; }; … … 9483 9480 7A0D7862220791ED00EBCF54 /* ResourceLoadStatisticsDatabaseStore.cpp */, 9484 9481 7A0D7861220791EC00EBCF54 /* ResourceLoadStatisticsDatabaseStore.h */, 9485 7AFBD36421E51BAB005DBACB /* ResourceLoadStatisticsMemoryStore.cpp */,9486 7AFBD36521E51BAB005DBACB /* ResourceLoadStatisticsMemoryStore.h */,9487 9482 7ACE82E8221CAE07000DA94C /* ResourceLoadStatisticsStore.cpp */, 9488 9483 7ACE82E7221CAE06000DA94C /* ResourceLoadStatisticsStore.h */, … … 12508 12503 6BE969CD1E54E054008B7483 /* ResourceLoadStatisticsClassifier.h in Headers */, 12509 12504 6BE969CB1E54D4CF008B7483 /* ResourceLoadStatisticsClassifierCocoa.h in Headers */, 12510 7AFBD36721E51BAB005DBACB /* ResourceLoadStatisticsMemoryStore.h in Headers */,12511 12505 1A30066E1110F4F70031937C /* ResponsivenessTimer.h in Headers */, 12512 12506 410482CE1DDD324F00F006D0 /* RTCNetwork.h in Headers */, -
trunk/Tools/ChangeLog
r282705 r282711 1 2021-09-17 Kate Cheney <katherine_cheney@apple.com> 2 3 Remove unnecessary ITP memory store code 4 https://bugs.webkit.org/show_bug.cgi?id=229512 5 <rdar://problem/82644309> 6 7 Reviewed by John Wilander. 8 9 * TestWebKitAPI/Tests/WebKitCocoa/ResourceLoadStatistics.mm: 10 (TEST): 11 Deleted an extra memory store test leftover from when we still had 12 a functioning memory store. Now it is just a duplicate of the database 13 test, so we can remove it. 14 1 15 2021-09-17 Alex Christensen <achristensen@webkit.org> 2 16 -
trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ResourceLoadStatistics.mm
r282034 r282711 81 81 } 82 82 83 TEST(ResourceLoadStatistics, GrandfatherCallback Database)83 TEST(ResourceLoadStatistics, GrandfatherCallback) 84 84 { 85 85 auto *dataStore = [WKWebsiteDataStore defaultDataStore]; … … 137 137 } 138 138 139 TEST(ResourceLoadStatistics, ShouldNotGrandfatherOnStartup Database)139 TEST(ResourceLoadStatistics, ShouldNotGrandfatherOnStartup) 140 140 { 141 141 auto *dataStore = [WKWebsiteDataStore defaultDataStore]; … … 158 158 } 159 159 160 TEST(ResourceLoadStatistics, ChildProcessesNotLaunched Database)160 TEST(ResourceLoadStatistics, ChildProcessesNotLaunched) 161 161 { 162 162 // Ensure the shared process pool exists so the data store operations we're about to do work with it. … … 680 680 681 681 __block bool doneFlag = false; 682 [dataStore _clearResourceLoadStatistics:^(void) {683 doneFlag = true;684 }];685 686 static bool statisticsUpdated = false;687 [dataStore _setResourceLoadStatisticsTestingCallback:^(WKWebsiteDataStore *, NSString *message) {688 if (![message isEqualToString:@"Statistics Updated"])689 return;690 statisticsUpdated = true;691 }];692 693 TestWebKitAPI::Util::run(&doneFlag);694 695 doneFlag = false;696 [dataStore _setThirdPartyCookieBlockingMode:true onlyOnSitesWithoutUserInteraction:false completionHandler: ^(void) {697 doneFlag = true;698 }];699 700 TestWebKitAPI::Util::run(&doneFlag);701 702 // Set two third parties to be prevalent, leave evil1.com as non-prevalent to ensure703 // this call returns all third parties.704 doneFlag = false;705 [dataStore _setPrevalentDomain:[NSURL URLWithString:@"http://evil2.com"] completionHandler: ^(void) {706 doneFlag = true;707 }];708 doneFlag = false;709 [dataStore _setPrevalentDomain:[NSURL URLWithString:@"http://evil3.com"] completionHandler: ^(void) {710 doneFlag = true;711 }];712 713 // Seed test data in the web process' observer.714 715 // evil1716 doneFlag = false;717 [sharedProcessPool _seedResourceLoadStatisticsForTestingWithFirstParty:[NSURL URLWithString:@"http://webkit.org"] thirdParty:[NSURL URLWithString:@"http://evil1.com"] shouldScheduleNotification:NO completionHandler: ^() {718 doneFlag = true;719 }];720 TestWebKitAPI::Util::run(&doneFlag);721 722 doneFlag = false;723 [sharedProcessPool _seedResourceLoadStatisticsForTestingWithFirstParty:[NSURL URLWithString:@"http://webkit2.org"] thirdParty:[NSURL URLWithString:@"http://evil1.com"] shouldScheduleNotification:NO completionHandler: ^() {724 doneFlag = true;725 }];726 TestWebKitAPI::Util::run(&doneFlag);727 728 729 // evil2730 doneFlag = false;731 [sharedProcessPool _seedResourceLoadStatisticsForTestingWithFirstParty:[NSURL URLWithString:@"http://webkit.org"] thirdParty:[NSURL URLWithString:@"http://evil2.com"] shouldScheduleNotification:NO completionHandler: ^() {732 doneFlag = true;733 }];734 TestWebKitAPI::Util::run(&doneFlag);735 736 737 // evil3738 doneFlag = false;739 [sharedProcessPool _seedResourceLoadStatisticsForTestingWithFirstParty:[NSURL URLWithString:@"http://webkit.org"] thirdParty:[NSURL URLWithString:@"http://evil3.com"] shouldScheduleNotification:NO completionHandler: ^() {740 doneFlag = true;741 }];742 TestWebKitAPI::Util::run(&doneFlag);743 doneFlag = false;744 [sharedProcessPool _seedResourceLoadStatisticsForTestingWithFirstParty:[NSURL URLWithString:@"http://webkit2.org"] thirdParty:[NSURL URLWithString:@"http://evil3.com"] shouldScheduleNotification:NO completionHandler: ^() {745 doneFlag = true;746 }];747 TestWebKitAPI::Util::run(&doneFlag);748 doneFlag = false;749 [sharedProcessPool _seedResourceLoadStatisticsForTestingWithFirstParty:[NSURL URLWithString:@"http://webkit3.org"] thirdParty:[NSURL URLWithString:@"http://evil3.com"] shouldScheduleNotification:NO completionHandler: ^() {750 doneFlag = true;751 }];752 TestWebKitAPI::Util::run(&doneFlag);753 754 statisticsUpdated = false;755 [webView1 loadHTMLString:@"<body><script>close();</script></body>" baseURL:[NSURL URLWithString:@"http://webkit.org"]];756 [webView2 loadHTMLString:@"<body><script>close();</script></body>" baseURL:[NSURL URLWithString:@"http://webkit2.org"]];757 [webView3 loadHTMLString:@"<body><script>close();</script></body>" baseURL:[NSURL URLWithString:@"http://webkit3.org"]];758 759 // Wait for the statistics to be updated in the network process.760 TestWebKitAPI::Util::run(&statisticsUpdated);761 762 // Check that the third-party evil1 is now registered as subresource.763 doneFlag = false;764 [dataStore _isRegisteredAsSubresourceUnderFirstParty:[NSURL URLWithString:@"http://webkit.org"] thirdParty:[NSURL URLWithString:@"http://evil1.com"] completionHandler: ^(BOOL isRegistered) {765 EXPECT_TRUE(isRegistered);766 doneFlag = true;767 }];768 TestWebKitAPI::Util::run(&doneFlag);769 doneFlag = false;770 [dataStore _isRegisteredAsSubresourceUnderFirstParty:[NSURL URLWithString:@"http://webkit2.org"] thirdParty:[NSURL URLWithString:@"http://evil1.com"] completionHandler: ^(BOOL isRegistered) {771 EXPECT_TRUE(isRegistered);772 doneFlag = true;773 }];774 TestWebKitAPI::Util::run(&doneFlag);775 776 777 // Check that the third-party evil2 is now registered as subresource.778 doneFlag = false;779 [dataStore _isRegisteredAsSubresourceUnderFirstParty:[NSURL URLWithString:@"http://webkit.org"] thirdParty:[NSURL URLWithString:@"http://evil2.com"] completionHandler: ^(BOOL isRegistered) {780 EXPECT_TRUE(isRegistered);781 doneFlag = true;782 }];783 TestWebKitAPI::Util::run(&doneFlag);784 785 786 // Check that the third-party evil3 is now registered as subresource.787 doneFlag = false;788 [dataStore _isRegisteredAsSubresourceUnderFirstParty:[NSURL URLWithString:@"http://webkit.org"] thirdParty:[NSURL URLWithString:@"http://evil3.com"] completionHandler: ^(BOOL isRegistered) {789 EXPECT_TRUE(isRegistered);790 doneFlag = true;791 }];792 TestWebKitAPI::Util::run(&doneFlag);793 doneFlag = false;794 [dataStore _isRegisteredAsSubresourceUnderFirstParty:[NSURL URLWithString:@"http://webkit2.org"] thirdParty:[NSURL URLWithString:@"http://evil3.com"] completionHandler: ^(BOOL isRegistered) {795 EXPECT_TRUE(isRegistered);796 doneFlag = true;797 }];798 TestWebKitAPI::Util::run(&doneFlag);799 doneFlag = false;800 [dataStore _isRegisteredAsSubresourceUnderFirstParty:[NSURL URLWithString:@"http://webkit3.org"] thirdParty:[NSURL URLWithString:@"http://evil3.com"] completionHandler: ^(BOOL isRegistered) {801 EXPECT_TRUE(isRegistered);802 doneFlag = true;803 }];804 TestWebKitAPI::Util::run(&doneFlag);805 806 807 // Collect the ITP data summary which should include all third parties in the808 // order: [evil3.com, evil1.com, evil2.com] sorted by number of first parties809 // it appears under or redirects to810 doneFlag = false;811 [dataStore _getResourceLoadStatisticsDataSummary:^void(NSArray<_WKResourceLoadStatisticsThirdParty *> *thirdPartyData)812 {813 EXPECT_EQ(static_cast<int>([thirdPartyData count]), 3);814 NSEnumerator *thirdPartyDomains = [thirdPartyData objectEnumerator];815 816 // evil3817 _WKResourceLoadStatisticsThirdParty *evil3ThirdParty = [thirdPartyDomains nextObject];818 EXPECT_WK_STREQ(evil3ThirdParty.thirdPartyDomain, @"evil3.com");819 820 NSEnumerator *evil3Enumerator = [evil3ThirdParty.underFirstParties objectEnumerator];821 _WKResourceLoadStatisticsFirstParty *evil3FirstParty1 = [evil3Enumerator nextObject];822 _WKResourceLoadStatisticsFirstParty *evil3FirstParty2 = [evil3Enumerator nextObject];823 _WKResourceLoadStatisticsFirstParty *evil3FirstParty3 = [evil3Enumerator nextObject];824 825 EXPECT_WK_STREQ(evil3FirstParty1.firstPartyDomain, @"webkit2.org");826 EXPECT_WK_STREQ(evil3FirstParty2.firstPartyDomain, @"webkit3.org");827 EXPECT_WK_STREQ(evil3FirstParty3.firstPartyDomain, @"webkit.org");828 829 EXPECT_FALSE(evil3FirstParty1.thirdPartyStorageAccessGranted);830 EXPECT_FALSE(evil3FirstParty2.thirdPartyStorageAccessGranted);831 EXPECT_FALSE(evil3FirstParty3.thirdPartyStorageAccessGranted);832 833 // evil1834 _WKResourceLoadStatisticsThirdParty *evil1ThirdParty = [thirdPartyDomains nextObject];835 EXPECT_WK_STREQ(evil1ThirdParty.thirdPartyDomain, @"evil1.com");836 837 NSEnumerator *evil1Enumerator = [evil1ThirdParty.underFirstParties objectEnumerator];838 _WKResourceLoadStatisticsFirstParty *evil1FirstParty1= [evil1Enumerator nextObject];839 _WKResourceLoadStatisticsFirstParty *evil1FirstParty2 = [evil1Enumerator nextObject];840 841 EXPECT_WK_STREQ(evil1FirstParty1.firstPartyDomain, @"webkit2.org");842 EXPECT_WK_STREQ(evil1FirstParty2.firstPartyDomain, @"webkit.org");843 844 EXPECT_FALSE(evil1FirstParty1.thirdPartyStorageAccessGranted);845 EXPECT_FALSE(evil1FirstParty2.thirdPartyStorageAccessGranted);846 847 // evil2848 _WKResourceLoadStatisticsThirdParty *evil2ThirdParty = [thirdPartyDomains nextObject];849 EXPECT_WK_STREQ(evil2ThirdParty.thirdPartyDomain, @"evil2.com");850 851 NSEnumerator *evil2Enumerator = [evil2ThirdParty.underFirstParties objectEnumerator];852 _WKResourceLoadStatisticsFirstParty *evil2FirstParty1 = [evil2Enumerator nextObject];853 854 EXPECT_WK_STREQ(evil2FirstParty1.firstPartyDomain, @"webkit.org");855 EXPECT_FALSE(evil2FirstParty1.thirdPartyStorageAccessGranted);856 doneFlag = true;857 }];858 859 TestWebKitAPI::Util::run(&doneFlag);860 }861 862 TEST(ResourceLoadStatistics, GetResourceLoadStatisticsDataSummaryDatabase)863 {864 auto *sharedProcessPool = [WKProcessPool _sharedProcessPool];865 auto *dataStore = [WKWebsiteDataStore defaultDataStore];866 867 auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);868 [configuration setProcessPool: sharedProcessPool];869 configuration.get().websiteDataStore = dataStore;870 871 auto webView1 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);872 auto webView2 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);873 auto webView3 = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);874 875 [webView1 loadHTMLString:@"WebKit Test" baseURL:[NSURL URLWithString:@"http://webkit.org"]];876 [webView1 _test_waitForDidFinishNavigation];877 [webView2 loadHTMLString:@"WebKit Test" baseURL:[NSURL URLWithString:@"http://webkit2.org"]];878 [webView2 _test_waitForDidFinishNavigation];879 [webView3 loadHTMLString:@"WebKit Test" baseURL:[NSURL URLWithString:@"http://webkit3.org"]];880 [webView3 _test_waitForDidFinishNavigation];881 882 [dataStore _setResourceLoadStatisticsEnabled:YES];883 884 __block bool doneFlag = false;885 682 [dataStore _setThirdPartyCookieBlockingMode:true onlyOnSitesWithoutUserInteraction:false completionHandler: ^(void) { 886 683 doneFlag = true;
Note:
See TracChangeset
for help on using the changeset viewer.