Diff
Modified: trunk/Source/WebKit/ChangeLog (257757 => 257758)
--- trunk/Source/WebKit/ChangeLog 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/ChangeLog 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,3 +1,47 @@
+2020-03-02 Brent Fulgham <[email protected]>
+
+ Add flag to indicate that ITP state was explicitly set
+ https://bugs.webkit.org/show_bug.cgi?id=208461
+ <rdar://problem/59960829>
+
+ Reviewed by John Wilander.
+
+ Now that ITP is supported in Ephemeral sessions, we would like to move to a process-wide
+ concept of ITP being on or off, rather than controlling this at a website data level.
+ This patch takes the first step by adding a flag to the WebKit::NetworkSessionCreationParameters
+ structure that tracks whether the state of ITP (On or Off) was explicitly set by
+ SPI (primarily during testing).
+
+ This patch also ensures that we can communicate with TCC for the purpose of checking if
+ ITP is on or off.
+
+ * NetworkProcess/NetworkSessionCreationParameters.cpp:
+ (WebKit::NetworkSessionCreationParameters::encode const):
+ (WebKit::NetworkSessionCreationParameters::decode):
+ * NetworkProcess/NetworkSessionCreationParameters.h:
+ * NetworkProcess/cocoa/NetworkSessionCocoa.mm:
+ (WebKit::NetworkSessionCocoa::NetworkSessionCocoa):
+ * NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in:
+ * Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb:
+ * Scripts/process-entitlements.sh:
+ * UIProcess/API/C/WKWebsiteDataStoreRef.cpp:
+ (WKWebsiteDataStoreSetResourceLoadStatisticsEnabled):
+ * UIProcess/API/Cocoa/WKWebsiteDataStore.mm:
+ (-[WKWebsiteDataStore _setResourceLoadStatisticsEnabled:]):
+ * UIProcess/Cocoa/VersionChecks.h:
+ * UIProcess/WebProcessPool.cpp:
+ (WebKit::WebProcessPool::WebProcessPool):
+ (WebKit::WebProcessPool::ensureNetworkProcess):
+ (WebKit::WebProcessPool::createNewWebProcess):
+ * UIProcess/WebProcessPool.h:
+ * UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm:
+ (WebKit::WebsiteDataStore::parameters):
+ * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+ (WebKit::WebsiteDataStore::setIsRunningResourceLoadStatisticsTest):
+ * UIProcess/WebsiteData/WebsiteDataStore.h:
+ (WebKit::WebsiteDataStore::itpStateWasExplicitlySet const):
+ (WebKit::WebsiteDataStore::useExplicitITPState):
+
2020-03-02 Alan Coon <[email protected]>
Add new Mac target numbers
Modified: trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp (257757 => 257758)
--- trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.cpp 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2018-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -63,6 +63,7 @@
encoder << resourceLoadStatisticsDirectory;
encoder << resourceLoadStatisticsDirectoryExtensionHandle;
encoder << enableResourceLoadStatistics;
+ encoder << isItpStateExplicitlySet;
encoder << enableResourceLoadStatisticsLogTestingEvent;
encoder << shouldIncludeLocalhostInResourceLoadStatistics;
encoder << enableResourceLoadStatisticsDebugMode;
@@ -179,6 +180,11 @@
if (!enableResourceLoadStatistics)
return WTF::nullopt;
+ Optional<bool> isItpStateExplicitlySet;
+ decoder >> isItpStateExplicitlySet;
+ if (!isItpStateExplicitlySet)
+ return WTF::nullopt;
+
Optional<bool> enableResourceLoadStatisticsLogTestingEvent;
decoder >> enableResourceLoadStatisticsLogTestingEvent;
if (!enableResourceLoadStatisticsLogTestingEvent)
@@ -300,6 +306,7 @@
, WTFMove(*resourceLoadStatisticsDirectory)
, WTFMove(*resourceLoadStatisticsDirectoryExtensionHandle)
, WTFMove(*enableResourceLoadStatistics)
+ , WTFMove(*isItpStateExplicitlySet)
, WTFMove(*enableResourceLoadStatisticsLogTestingEvent)
, WTFMove(*shouldIncludeLocalhostInResourceLoadStatistics)
, WTFMove(*enableResourceLoadStatisticsDebugMode)
Modified: trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h (257757 => 257758)
--- trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSessionCreationParameters.h 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -82,6 +82,7 @@
String resourceLoadStatisticsDirectory;
SandboxExtension::Handle resourceLoadStatisticsDirectoryExtensionHandle;
bool enableResourceLoadStatistics { false };
+ bool isItpStateExplicitlySet { false };
bool enableResourceLoadStatisticsLogTestingEvent { false };
bool shouldIncludeLocalhostInResourceLoadStatistics { true };
bool enableResourceLoadStatisticsDebugMode { false };
Modified: trunk/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm (257757 => 257758)
--- trunk/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -1161,7 +1161,7 @@
#endif
#if HAVE(SESSION_CLEANUP)
- activateSessionCleanup(*this);
+ activateSessionCleanup(*this, parameters);
#endif
}
Modified: trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in (257757 => 257758)
--- trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in 2020-03-03 03:11:06 UTC (rev 257758)
@@ -440,3 +440,6 @@
(allow mach-lookup
(global-name "com.apple.ProgressReporting"))
+;; Needed for TCC.
+(allow mach-lookup
+ (global-name "com.apple.tccd"))
Modified: trunk/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb (257757 => 257758)
--- trunk/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb 2020-03-03 03:11:06 UTC (rev 257758)
@@ -674,3 +674,7 @@
(well-known-system-group-container-literal "/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist"))
(allow iokit-get-properties
(iokit-property "IORegistryEntryPropertyKeys"))
+
+;; Needed for TCC.
+(allow mach-lookup
+ (global-name "com.apple.tccd"))
Modified: trunk/Source/WebKit/Scripts/process-entitlements.sh (257757 => 257758)
--- trunk/Source/WebKit/Scripts/process-entitlements.sh 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/Scripts/process-entitlements.sh 2020-03-03 03:11:06 UTC (rev 257758)
@@ -45,6 +45,12 @@
plistbuddy Add :com.apple.private.network.socket-delegate bool YES
fi
+ if (( "${TARGET_MAC_OS_X_VERSION_MAJOR}" >= 101600 ))
+ then
+ plistbuddy Add :com.apple.private.tcc.manager.check-by-audit-token array
+ plistbuddy Add :com.apple.private.tcc.manager.check-by-audit-token:0 string kTCCServiceWebKitIntelligentTrackingPrevention
+ fi
+
plistbuddy Add :com.apple.rootless.storage.WebKitNetworkingSandbox bool YES
fi
}
@@ -101,6 +107,9 @@
{
plistbuddy Add :com.apple.private.network.socket-delegate bool YES
plistbuddy Add :com.apple.security.network.client bool YES
+
+ plistbuddy Add :com.apple.private.tcc.manager.check-by-audit-token array
+ plistbuddy Add :com.apple.private.tcc.manager.check-by-audit-token:0 string kTCCServiceWebKitIntelligentTrackingPrevention
}
function maccatalyst_process_plugin_entitlements()
@@ -168,6 +177,9 @@
plistbuddy Add :com.apple.private.memorystatus bool YES
plistbuddy Add :com.apple.private.network.socket-delegate bool YES
+ plistbuddy Add :com.apple.private.tcc.manager.check-by-audit-token array
+ plistbuddy Add :com.apple.private.tcc.manager.check-by-audit-token:0 string kTCCServiceWebKitIntelligentTrackingPrevention
+
plistbuddy Add :seatbelt-profiles array
plistbuddy Add :seatbelt-profiles:0 string com.apple.WebKit.Networking
}
Modified: trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp (257757 => 257758)
--- trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/UIProcess/API/C/WKWebsiteDataStoreRef.cpp 2020-03-03 03:11:06 UTC (rev 257758)
@@ -68,7 +68,11 @@
void WKWebsiteDataStoreSetResourceLoadStatisticsEnabled(WKWebsiteDataStoreRef dataStoreRef, bool enable)
{
- WebKit::toImpl(dataStoreRef)->setResourceLoadStatisticsEnabled(enable);
+ auto* websiteDataStore = WebKit::toImpl(dataStoreRef);
+#if ENABLE(RESOURCE_LOAD_STATISTICS)
+ websiteDataStore->useExplicitITPState();
+#endif
+ websiteDataStore->setResourceLoadStatisticsEnabled(enable);
}
void WKWebsiteDataStoreIsStatisticsEphemeral(WKWebsiteDataStoreRef dataStoreRef, void* context, WKWebsiteDataStoreStatisticsEphemeralFunction completionHandler)
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm (257757 => 257758)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm 2020-03-03 03:11:06 UTC (rev 257758)
@@ -278,6 +278,7 @@
- (void)_setResourceLoadStatisticsEnabled:(BOOL)enabled
{
+ _websiteDataStore->useExplicitITPState();
_websiteDataStore->setResourceLoadStatisticsEnabled(enabled);
}
Modified: trunk/Source/WebKit/UIProcess/Cocoa/VersionChecks.h (257757 => 257758)
--- trunk/Source/WebKit/UIProcess/Cocoa/VersionChecks.h 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/UIProcess/Cocoa/VersionChecks.h 2020-03-03 03:11:06 UTC (rev 257758)
@@ -46,6 +46,11 @@
#ifndef DYLD_IOS_VERSION_FIRST_WITH_DEVICE_ORIENTATION_AND_MOTION_PERMISSION_API
#define DYLD_IOS_VERSION_FIRST_WITH_DEVICE_ORIENTATION_AND_MOTION_PERMISSION_API 0
#endif
+
+#ifndef DYLD_IOS_VERSION_FIRST_WITH_SESSION_CLEANUP_BY_DEFAULT
+#define DYLD_IOS_VERSION_FIRST_WITH_SESSION_CLEANUP_BY_DEFAULT 0
+#endif
+
#endif // PLATFORM(IOS_FAMILY)
#if PLATFORM(MAC)
@@ -52,8 +57,12 @@
#ifndef DYLD_MACOS_VERSION_FIRST_WITH_EXCEPTIONS_FOR_RELATED_WEBVIEWS_USING_DIFFERENT_DATA_STORES
#define DYLD_MACOS_VERSION_FIRST_WITH_EXCEPTIONS_FOR_RELATED_WEBVIEWS_USING_DIFFERENT_DATA_STORES 0
#endif
+
+#ifndef DYLD_MACOS_VERSION_FIRST_WITH_SESSION_CLEANUP_BY_DEFAULT
+#define DYLD_MACOS_VERSION_FIRST_WITH_SESSION_CLEANUP_BY_DEFAULT 0
#endif
+#endif // PLATFORM(MAC)
namespace WebKit {
@@ -82,6 +91,7 @@
FirstThatSupportsOverflowHiddenOnMainFrame = DYLD_IOS_VERSION_13_0,
FirstWhereSiteSpecificQuirksAreEnabledByDefault = DYLD_IOS_VERSION_13_2,
FirstThatRestrictsBaseURLSchemes = DYLD_IOS_VERSION_13_4,
+ FirstWithSessionCleanupByDefault = DYLD_IOS_VERSION_FIRST_WITH_SESSION_CLEANUP_BY_DEFAULT,
#elif PLATFORM(MAC)
FirstWithNetworkCache = DYLD_MACOSX_VERSION_10_11,
FirstWithExceptionsForDuplicateCompletionHandlerCalls = DYLD_MACOSX_VERSION_10_13,
@@ -93,6 +103,7 @@
FirstWithExceptionsForRelatedWebViewsUsingDifferentDataStores = DYLD_MACOS_VERSION_FIRST_WITH_EXCEPTIONS_FOR_RELATED_WEBVIEWS_USING_DIFFERENT_DATA_STORES,
FirstWhereSiteSpecificQuirksAreEnabledByDefault = DYLD_MACOSX_VERSION_10_15_1,
FirstThatRestrictsBaseURLSchemes = DYLD_MACOSX_VERSION_10_15_4,
+ FirstWithSessionCleanupByDefault = DYLD_MACOS_VERSION_FIRST_WITH_SESSION_CLEANUP_BY_DEFAULT,
#endif
};
Modified: trunk/Source/WebKit/UIProcess/WebProcessPool.cpp (257757 => 257758)
--- trunk/Source/WebKit/UIProcess/WebProcessPool.cpp 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/UIProcess/WebProcessPool.cpp 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2010-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -136,6 +136,14 @@
#include <wtf/RefCountedLeakCounter.h>
#endif
+#if USE(APPLE_INTERNAL_SDK)
+#include <WebKitAdditions/WebProcessPoolAdditions.h>
+#else
+#define WEB_PROCESS_POOL_ADDITIONS
+#define WEB_PROCESS_POOL_ADDITIONS_2
+#define WEB_PROCESS_POOL_ADDITIONS_3
+#endif
+
#define WEBPROCESSPOOL_RELEASE_LOG(channel, fmt, ...) RELEASE_LOG(channel, "%p - WebProcessPool::" fmt, this, ##__VA_ARGS__)
#define WEBPROCESSPOOL_RELEASE_LOG_ERROR(channel, fmt, ...) RELEASE_LOG_ERROR(channel, "%p - WebProcessPool::" fmt, this, ##__VA_ARGS__)
#define WEBPROCESSPOOL_RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), channel, "%p - WebProcessPool::" fmt, this, ##__VA_ARGS__)
@@ -241,6 +249,7 @@
#endif
, m_alwaysRunsAtBackgroundPriority(m_configuration->alwaysRunsAtBackgroundPriority())
, m_shouldTakeUIBackgroundAssertion(m_configuration->shouldTakeUIBackgroundAssertion())
+ WEB_PROCESS_POOL_ADDITIONS
, m_userObservablePageCounter([this](RefCounterEvent) { updateProcessSuppressionState(); })
, m_processSuppressionDisabledForPageCounter([this](RefCounterEvent) { updateProcessSuppressionState(); })
, m_hiddenPageThrottlingAutoIncreasesCounter([this](RefCounterEvent) { m_hiddenPageThrottlingTimer.startOneShot(0_s); })
@@ -585,6 +594,7 @@
#endif
WebCore::FirstPartyWebsiteDataRemovalMode firstPartyWebsiteDataRemovalMode = WebCore::FirstPartyWebsiteDataRemovalMode::AllButCookies;
WebCore::RegistrableDomain manualPrevalentResource { };
+ WEB_PROCESS_POOL_ADDITIONS_2
if (withWebsiteDataStore) {
enableResourceLoadStatistics = withWebsiteDataStore->resourceLoadStatisticsEnabled();
#if ENABLE(RESOURCE_LOAD_STATISTICS)
@@ -832,6 +842,7 @@
WebProcessProxy& WebProcessPool::createNewWebProcess(WebsiteDataStore* websiteDataStore, WebProcessProxy::IsPrewarmed isPrewarmed)
{
+ WEB_PROCESS_POOL_ADDITIONS_3
auto processProxy = WebProcessProxy::create(*this, websiteDataStore, isPrewarmed);
auto& process = processProxy.get();
initializeNewWebProcess(process, websiteDataStore, isPrewarmed);
Modified: trunk/Source/WebKit/UIProcess/WebProcessPool.h (257757 => 257758)
--- trunk/Source/WebKit/UIProcess/WebProcessPool.h 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/UIProcess/WebProcessPool.h 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2010-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -728,6 +728,7 @@
bool m_shouldTakeUIBackgroundAssertion;
bool m_shouldMakeNextWebProcessLaunchFailForTesting { false };
bool m_shouldMakeNextNetworkProcessLaunchFailForTesting { false };
+ bool m_tccPreferenceEnabled { false };
UserObservablePageCounter m_userObservablePageCounter;
ProcessSuppressionDisabledCounter m_processSuppressionDisabledForPageCounter;
Modified: trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm (257757 => 257758)
--- trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -162,6 +162,7 @@
WTFMove(resourceLoadStatisticsDirectory),
WTFMove(resourceLoadStatisticsDirectoryHandle),
resourceLoadStatisticsEnabled(),
+ isItpStateExplicitlySet(),
hasStatisticsTestingCallback(),
shouldIncludeLocalhostInResourceLoadStatistics,
enableResourceLoadStatisticsDebugMode,
Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp (257757 => 257758)
--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -1599,6 +1599,7 @@
void WebsiteDataStore::setIsRunningResourceLoadStatisticsTest(bool value, CompletionHandler<void()>&& completionHandler)
{
+ useExplicitITPState();
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
for (auto& processPool : processPools())
Modified: trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h (257757 => 257758)
--- trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h 2020-03-03 03:03:47 UTC (rev 257757)
+++ trunk/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h 2020-03-03 03:11:06 UTC (rev 257758)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -196,6 +196,8 @@
void setResourceLoadStatisticsShouldBlockThirdPartyCookiesForTesting(bool enabled, bool onlyOnSitesWithoutUserInteraction, CompletionHandler<void()>&&);
void setResourceLoadStatisticsFirstPartyWebsiteDataRemovalModeForTesting(bool enabled, CompletionHandler<void()>&&);
WebCore::ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode() const;
+ bool isItpStateExplicitlySet() const { return m_isItpStateExplicitlySet; }
+ void useExplicitITPState() { m_isItpStateExplicitlySet = true; }
#endif
void setCacheMaxAgeCapForPrevalentResources(Seconds, CompletionHandler<void()>&&);
void resetCacheMaxAgeCapForPrevalentResources(CompletionHandler<void()>&&);
@@ -337,6 +339,8 @@
WeakHashSet<WebProcessProxy> m_processes;
+ bool m_isItpStateExplicitlySet { false };
+
#if HAVE(SEC_KEY_PROXY)
Vector<Ref<SecKeyProxyStore>> m_secKeyProxyStores;
#endif