Changeset 224791 in webkit
- Timestamp:
- Nov 13, 2017, 3:50:47 PM (7 years ago)
- Location:
- trunk/Source
- Files:
-
- 26 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r224789 r224791 1 2017-11-13 Alex Christensen <achristensen@webkit.org> 2 3 Merge NetworkProcess::EnsurePrivateBrowsingSession and NetworkProcess::AddWebsiteDataStore into one message type 4 https://bugs.webkit.org/show_bug.cgi?id=178751 5 6 Reviewed by Brady Eidson. 7 8 The creation of private browsing sessions and persistent browsing sessions has evolved to be the same thing 9 with differences in parameters, like no sandbox extensions or paths to persistent storage locations for private browsing. 10 These should be united to simplify future development of WebsiteDataStore construction. 11 12 * platform/network/NetworkStorageSession.h: 13 * platform/network/cf/NetworkStorageSessionCFNet.cpp: 14 (WebCore::NetworkStorageSession::ensurePrivateBrowsingSession): Deleted. 15 * platform/network/soup/NetworkStorageSessionSoup.cpp: 16 (WebCore::NetworkStorageSession::ensureSession): 17 (WebCore::NetworkStorageSession::ensurePrivateBrowsingSession): Deleted. 18 1 19 2017-11-07 Brian Burg <bburg@apple.com> 2 20 -
trunk/Source/WebCore/platform/network/NetworkStorageSession.h
r223207 r224791 64 64 WEBCORE_EXPORT static NetworkStorageSession& defaultStorageSession(); 65 65 WEBCORE_EXPORT static NetworkStorageSession* storageSession(PAL::SessionID); 66 WEBCORE_EXPORT static void ensurePrivateBrowsingSession(PAL::SessionID, const String& identifierBase = String());67 66 WEBCORE_EXPORT static void ensureSession(PAL::SessionID, const String& identifierBase = String()); 68 67 WEBCORE_EXPORT static void destroySession(PAL::SessionID); -
trunk/Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp
r222898 r224791 109 109 } 110 110 111 void NetworkStorageSession::ensurePrivateBrowsingSession(PAL::SessionID sessionID, const String& identifierBase)112 {113 ASSERT(sessionID.isEphemeral());114 ensureSession(sessionID, identifierBase);115 }116 117 111 void NetworkStorageSession::ensureSession(PAL::SessionID sessionID, const String& identifierBase, RetainPtr<CFHTTPCookieStorageRef>&& cookieStorage) 118 112 { -
trunk/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp
r222706 r224791 80 80 } 81 81 82 void NetworkStorageSession::ensurePrivateBrowsingSession(PAL::SessionID sessionID, const String&) 83 { 84 ASSERT(sessionID != PAL::SessionID::defaultSessionID()); 82 void NetworkStorageSession::ensureSession(PAL::SessionID sessionID, const String&) 83 { 85 84 ASSERT(!globalSessionMap().contains(sessionID)); 86 85 globalSessionMap().add(sessionID, std::make_unique<NetworkStorageSession>(sessionID, std::make_unique<SoupNetworkSession>(sessionID))); 87 }88 89 void NetworkStorageSession::ensureSession(PAL::SessionID, const String&)90 {91 // FIXME: Implement92 86 } 93 87 -
trunk/Source/WebKit/ChangeLog
r224790 r224791 1 2017-11-13 Alex Christensen <achristensen@webkit.org> 2 3 Merge NetworkProcess::EnsurePrivateBrowsingSession and NetworkProcess::AddWebsiteDataStore into one message type 4 https://bugs.webkit.org/show_bug.cgi?id=178751 5 6 Reviewed by Brady Eidson. 7 8 * NetworkProcess/NetworkConnectionToWebProcess.cpp: 9 (WebKit::NetworkConnectionToWebProcess::ensureLegacyPrivateBrowsingSession): 10 * NetworkProcess/NetworkProcess.cpp: 11 (WebKit::NetworkProcess::initializeNetworkProcess): 12 (WebKit::NetworkProcess::ensurePrivateBrowsingSession): Deleted. 13 * NetworkProcess/NetworkProcess.h: 14 * NetworkProcess/NetworkProcess.messages.in: 15 * NetworkProcess/RemoteNetworkingContext.h: 16 * NetworkProcess/mac/RemoteNetworkingContext.mm: 17 (WebKit::RemoteNetworkingContext::ensureWebsiteDataStoreSession): 18 (WebKit::RemoteNetworkingContext::ensurePrivateBrowsingSession): Deleted. 19 * Shared/WebsiteDataStoreParameters.cpp: 20 (WebKit::WebsiteDataStoreParameters::legacyPrivateSessionParameters): 21 * Shared/WebsiteDataStoreParameters.h: 22 * UIProcess/WebProcessPool.cpp: 23 (WebKit::WebProcessPool::setAnyPageGroupMightHavePrivateBrowsingEnabled): 24 (WebKit::WebProcessPool::createNewWebProcess): 25 (WebKit::WebProcessPool::pageAddedToProcess): 26 * WebProcess/InjectedBundle/InjectedBundle.cpp: 27 (WebKit::InjectedBundle::setPrivateBrowsingEnabled): 28 * WebProcess/WebCoreSupport/mac/WebFrameNetworkingContext.h: 29 * WebProcess/WebCoreSupport/mac/WebFrameNetworkingContext.mm: 30 (WebKit::WebFrameNetworkingContext::ensureWebsiteDataStoreSession): 31 (WebKit::WebFrameNetworkingContext::ensurePrivateBrowsingSession): Deleted. 32 * WebProcess/WebPage/WebPage.cpp: 33 (WebKit::WebPage::setSessionID): 34 * WebProcess/WebProcess.cpp: 35 (WebKit::WebProcess::ensurePrivateBrowsingSession): Deleted. 36 * WebProcess/WebProcess.h: 37 * WebProcess/WebProcess.messages.in: 38 1 39 2017-11-13 Alex Christensen <achristensen@webkit.org> 2 40 -
trunk/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
r224267 r224791 469 469 void NetworkConnectionToWebProcess::ensureLegacyPrivateBrowsingSession() 470 470 { 471 NetworkProcess::singleton(). ensurePrivateBrowsingSession({ { }, { }, { }, { }, WebsiteDataStore::defaultCacheStoragePerOriginQuota, { }, { PAL::SessionID::legacyPrivateSessionID(), { }, { }, AllowsCellularAccess::Yes }});471 NetworkProcess::singleton().addWebsiteDataStore(WebsiteDataStoreParameters::legacyPrivateSessionParameters()); 472 472 } 473 473 -
trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp
r224371 r224791 239 239 // FIXME: instead of handling this here, a message should be sent later (scales to multiple sessions) 240 240 if (parameters.privateBrowsingEnabled) 241 RemoteNetworkingContext::ensure PrivateBrowsingSession({ { }, { }, { }, { }, WebsiteDataStore::defaultCacheStoragePerOriginQuota, { }, { PAL::SessionID::legacyPrivateSessionID(), { }, { }, AllowsCellularAccess::Yes }});241 RemoteNetworkingContext::ensureWebsiteDataStoreSession(WebsiteDataStoreParameters::legacyPrivateSessionParameters()); 242 242 243 243 if (parameters.shouldUseTestingNetworkSession) … … 296 296 ASSERT_NOT_REACHED(); 297 297 #endif 298 }299 300 void NetworkProcess::ensurePrivateBrowsingSession(WebsiteDataStoreParameters&& parameters)301 {302 RemoteNetworkingContext::ensurePrivateBrowsingSession(WTFMove(parameters));303 298 } 304 299 -
trunk/Source/WebKit/NetworkProcess/NetworkProcess.h
r223730 r224791 133 133 void prefetchDNS(const String&); 134 134 135 void ensurePrivateBrowsingSession(WebsiteDataStoreParameters&&);135 void addWebsiteDataStore(WebsiteDataStoreParameters&&); 136 136 137 137 void grantSandboxExtensionsToStorageProcessForBlobs(const Vector<String>& filenames, Function<void ()>&& completionHandler); … … 188 188 void initializeNetworkProcess(NetworkProcessCreationParameters&&); 189 189 void createNetworkConnectionToWebProcess(); 190 void addWebsiteDataStore(WebsiteDataStoreParameters&&);191 190 void destroySession(PAL::SessionID); 192 191 -
trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in
r223730 r224791 36 36 ClearCachedCredentials() 37 37 38 EnsurePrivateBrowsingSession(struct WebKit::WebsiteDataStoreParameters websiteDataStoreParameters)39 38 AddWebsiteDataStore(struct WebKit::WebsiteDataStoreParameters websiteDataStoreParameters); 40 39 DestroySession(PAL::SessionID sessionID) -
trunk/Source/WebKit/NetworkProcess/RemoteNetworkingContext.h
r220887 r224791 44 44 45 45 // FIXME: Remove platform-specific code and use SessionTracker. 46 static void ensurePrivateBrowsingSession(WebsiteDataStoreParameters&&);47 46 static void ensureWebsiteDataStoreSession(WebsiteDataStoreParameters&&); 48 47 -
trunk/Source/WebKit/NetworkProcess/mac/RemoteNetworkingContext.mm
r223791 r224791 82 82 } 83 83 84 void RemoteNetworkingContext::ensurePrivateBrowsingSession(WebsiteDataStoreParameters&& parameters)85 {86 auto sessionID = parameters.networkSessionParameters.sessionID;87 ASSERT(sessionID.isEphemeral());88 89 if (NetworkStorageSession::storageSession(sessionID))90 return;91 92 String base;93 if (SessionTracker::getIdentifierBase().isNull())94 base = [[NSBundle mainBundle] bundleIdentifier];95 else96 base = SessionTracker::getIdentifierBase();97 98 NetworkStorageSession::ensurePrivateBrowsingSession(sessionID, base + '.' + String::number(sessionID.sessionID()));99 100 auto* session = NetworkStorageSession::storageSession(sessionID);101 for (const auto& cookie : parameters.pendingCookies)102 session->setCookie(cookie);103 104 #if USE(NETWORK_SESSION)105 parameters.networkSessionParameters.legacyCustomProtocolManager = NetworkProcess::singleton().supplement<LegacyCustomProtocolManager>();106 SessionTracker::setSession(sessionID, NetworkSession::create(WTFMove(parameters.networkSessionParameters)));107 #endif108 }109 110 84 void RemoteNetworkingContext::ensureWebsiteDataStoreSession(WebsiteDataStoreParameters&& parameters) 111 85 { … … 120 94 base = SessionTracker::getIdentifierBase(); 121 95 122 SandboxExtension::consumePermanently(parameters.cookieStoragePathExtensionHandle); 96 if (!sessionID.isEphemeral()) { 97 bool result = SandboxExtension::consumePermanently(parameters.cookieStoragePathExtensionHandle); 98 ASSERT_UNUSED(result, result); 99 } 123 100 124 101 RetainPtr<CFHTTPCookieStorageRef> uiProcessCookieStorage; -
trunk/Source/WebKit/NetworkProcess/soup/RemoteNetworkingContextSoup.cpp
r223791 r224791 49 49 } 50 50 51 void RemoteNetworkingContext::ensure PrivateBrowsingSession(WebsiteDataStoreParameters&& parameters)51 void RemoteNetworkingContext::ensureWebsiteDataStoreSession(WebsiteDataStoreParameters&& parameters) 52 52 { 53 53 auto sessionID = parameters.networkSessionParameters.sessionID; 54 ASSERT(sessionID.isEphemeral());55 56 54 if (NetworkStorageSession::storageSession(sessionID)) 57 55 return; 58 56 59 NetworkStorageSession::ensure PrivateBrowsingSession(sessionID, String::number(sessionID.sessionID()));57 NetworkStorageSession::ensureSession(sessionID, String::number(sessionID.sessionID())); 60 58 SessionTracker::setSession(sessionID, NetworkSession::create(WTFMove(parameters.networkSessionParameters))); 61 }62 63 void RemoteNetworkingContext::ensureWebsiteDataStoreSession(WebsiteDataStoreParameters&&)64 {65 // FIXME: Implement.66 59 } 67 60 -
trunk/Source/WebKit/Shared/WebsiteDataStoreParameters.cpp
r223791 r224791 28 28 29 29 #include "WebCoreArgumentCoders.h" 30 #include "WebsiteDataStore.h" 30 31 31 32 namespace WebKit { … … 78 79 } 79 80 81 WebsiteDataStoreParameters WebsiteDataStoreParameters::legacyPrivateSessionParameters() 82 { 83 return { { }, { }, { }, { }, WebsiteDataStore::defaultCacheStoragePerOriginQuota, { }, { PAL::SessionID::legacyPrivateSessionID(), { }, { }, { }}}; 84 } 80 85 81 86 } // namespace WebKit -
trunk/Source/WebKit/Shared/WebsiteDataStoreParameters.h
r223791 r224791 44 44 WebsiteDataStoreParameters(WebsiteDataStoreParameters&&) = default; 45 45 ~WebsiteDataStoreParameters(); 46 static WebsiteDataStoreParameters legacyPrivateSessionParameters(); 46 47 47 48 void encode(IPC::Encoder&) const; -
trunk/Source/WebKit/UIProcess/WebProcessPool.cpp
r224462 r224791 635 635 if (networkProcess()) { 636 636 if (privateBrowsingEnabled) 637 networkProcess()->send(Messages::NetworkProcess:: EnsurePrivateBrowsingSession({ { }, { }, { }, { }, WebsiteDataStore::defaultCacheStoragePerOriginQuota, { }, { PAL::SessionID::legacyPrivateSessionID(), { }, { }, AllowsCellularAccess::Yes }}), 0);637 networkProcess()->send(Messages::NetworkProcess::AddWebsiteDataStore(WebsiteDataStoreParameters::legacyPrivateSessionParameters()), 0); 638 638 else 639 639 networkProcess()->send(Messages::NetworkProcess::DestroySession(PAL::SessionID::legacyPrivateSessionID()), 0); … … 641 641 642 642 if (privateBrowsingEnabled) 643 sendToAllProcesses(Messages::WebProcess:: EnsurePrivateBrowsingSession(PAL::SessionID::legacyPrivateSessionID()));643 sendToAllProcesses(Messages::WebProcess::AddWebsiteDataStore(WebsiteDataStoreParameters::legacyPrivateSessionParameters())); 644 644 else 645 645 sendToAllProcesses(Messages::WebProcess::DestroySession(PAL::SessionID::legacyPrivateSessionID())); … … 832 832 833 833 if (WebPreferences::anyPagesAreUsingPrivateBrowsing()) 834 process.send(Messages::WebProcess:: EnsurePrivateBrowsingSession(PAL::SessionID::legacyPrivateSessionID()), 0);834 process.send(Messages::WebProcess::AddWebsiteDataStore(WebsiteDataStoreParameters::legacyPrivateSessionParameters()), 0); 835 835 836 836 if (m_automationSession) … … 1022 1022 auto sessionID = page.sessionID(); 1023 1023 if (sessionID.isEphemeral()) { 1024 // FIXME: Merge NetworkProcess::EnsurePrivateBrowsingSession and NetworkProcess::AddWebsiteDataStore into one message type.1025 // They do basically the same thing.1026 1024 ASSERT(page.websiteDataStore().parameters().networkSessionParameters.sessionID == sessionID); 1027 sendToNetworkingProcess(Messages::NetworkProcess:: EnsurePrivateBrowsingSession(page.websiteDataStore().parameters()));1028 page.process().send(Messages::WebProcess:: EnsurePrivateBrowsingSession(sessionID), 0);1025 sendToNetworkingProcess(Messages::NetworkProcess::AddWebsiteDataStore(page.websiteDataStore().parameters())); 1026 page.process().send(Messages::WebProcess::AddWebsiteDataStore({{ }, { }, { }, { }, { }, { }, { sessionID, { }, { }, { }}}), 0); 1029 1027 } else if (sessionID != PAL::SessionID::defaultSessionID()) { 1030 1028 sendToNetworkingProcess(Messages::NetworkProcess::AddWebsiteDataStore(page.websiteDataStore().parameters())); -
trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp
r223791 r224791 322 322 if (enabled) { 323 323 WebProcess::singleton().ensureLegacyPrivateBrowsingSessionInNetworkProcess(); 324 WebFrameNetworkingContext::ensure PrivateBrowsingSession({ { }, { }, { }, { }, { }, { }, { PAL::SessionID::legacyPrivateSessionID(), { }, { }, AllowsCellularAccess::Yes }});324 WebFrameNetworkingContext::ensureWebsiteDataStoreSession({ { }, { }, { }, { }, { }, { }, { PAL::SessionID::legacyPrivateSessionID(), { }, { }, AllowsCellularAccess::Yes }}); 325 325 } else 326 326 SessionTracker::destroySession(PAL::SessionID::legacyPrivateSessionID()); -
trunk/Source/WebKit/WebProcess/WebCoreSupport/mac/WebFrameNetworkingContext.h
r223791 r224791 43 43 44 44 // FIXME: remove platform-specific code and use SessionTracker 45 static void ensurePrivateBrowsingSession(WebsiteDataStoreParameters&&);46 45 static void ensureWebsiteDataStoreSession(WebsiteDataStoreParameters&&); 47 46 -
trunk/Source/WebKit/WebProcess/WebCoreSupport/mac/WebFrameNetworkingContext.mm
r223791 r224791 46 46 47 47 namespace WebKit { 48 49 void WebFrameNetworkingContext::ensurePrivateBrowsingSession(WebsiteDataStoreParameters&& parameters)50 {51 auto sessionID = parameters.networkSessionParameters.sessionID;52 ASSERT(sessionID.isEphemeral());53 54 if (WebCore::NetworkStorageSession::storageSession(sessionID))55 return;56 57 String base;58 if (SessionTracker::getIdentifierBase().isNull())59 base = [[NSBundle mainBundle] bundleIdentifier];60 else61 base = SessionTracker::getIdentifierBase();62 63 NetworkStorageSession::ensurePrivateBrowsingSession(sessionID, base + '.' + String::number(sessionID.sessionID()));64 #if USE(NETWORK_SESSION)65 SessionTracker::setSession(sessionID, NetworkSession::create(WTFMove(parameters.networkSessionParameters)));66 #endif67 }68 48 69 49 void WebFrameNetworkingContext::ensureWebsiteDataStoreSession(WebsiteDataStoreParameters&& parameters) … … 79 59 base = SessionTracker::getIdentifierBase(); 80 60 81 SandboxExtension::consumePermanently(parameters.cookieStoragePathExtensionHandle); 61 if (!sessionID.isEphemeral()) { 62 bool result = SandboxExtension::consumePermanently(parameters.cookieStoragePathExtensionHandle); 63 ASSERT_UNUSED(result, result); 64 } 82 65 83 RetainPtr<CFHTTPCookieStorageRef> uiProcessCookieStorage = cookieStorageFromIdentifyingData(parameters.uiProcessCookieStorageIdentifier); 66 RetainPtr<CFHTTPCookieStorageRef> uiProcessCookieStorage; 67 if (!sessionID.isEphemeral()) 68 uiProcessCookieStorage = cookieStorageFromIdentifyingData(parameters.uiProcessCookieStorageIdentifier); 69 else 70 ASSERT(parameters.uiProcessCookieStorageIdentifier.isEmpty()); 84 71 85 72 NetworkStorageSession::ensureSession(sessionID, base + '.' + String::number(sessionID.sessionID()), WTFMove(uiProcessCookieStorage)); -
trunk/Source/WebKit/WebProcess/WebCoreSupport/soup/WebFrameNetworkingContext.cpp
r223791 r224791 44 44 namespace WebKit { 45 45 46 void WebFrameNetworkingContext::ensure PrivateBrowsingSession(WebsiteDataStoreParameters&& parameters)46 void WebFrameNetworkingContext::ensureWebsiteDataStoreSession(WebsiteDataStoreParameters&& parameters) 47 47 { 48 48 auto sessionID = parameters.networkSessionParameters.sessionID; 49 49 ASSERT(RunLoop::isMain()); 50 ASSERT(sessionID.isEphemeral());51 50 52 51 if (NetworkStorageSession::storageSession(sessionID)) 53 52 return; 54 53 55 NetworkStorageSession::ensure PrivateBrowsingSession(sessionID, String::number(sessionID.sessionID()));54 NetworkStorageSession::ensureSession(sessionID, String::number(sessionID.sessionID())); 56 55 SessionTracker::setSession(sessionID, NetworkSession::create(WTFMove(parameters.networkSessionParameters))); 57 }58 59 void WebFrameNetworkingContext::ensureWebsiteDataStoreSession(WebsiteDataStoreParameters&&)60 {61 // FIXME: Implement62 56 } 63 57 -
trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp
r224755 r224791 122 122 #include "WebUserMediaClient.h" 123 123 #include "WebValidationMessageClient.h" 124 #include "WebsiteDataStoreParameters.h" 124 125 #include <JavaScriptCore/APICast.h> 125 126 #include <WebCore/ApplicationCacheStorage.h> … … 2741 2742 { 2742 2743 if (sessionID.isEphemeral()) 2743 WebProcess::singleton(). ensurePrivateBrowsingSession(sessionID);2744 WebProcess::singleton().addWebsiteDataStore({{ }, { }, { }, { }, { }, { }, { sessionID, { }, { }, { }}}); 2744 2745 m_page->setSessionID(sessionID); 2745 2746 } -
trunk/Source/WebKit/WebProcess/WebProcess.cpp
r224041 r224791 516 516 } 517 517 518 void WebProcess::ensurePrivateBrowsingSession(PAL::SessionID sessionID)519 {520 WebFrameNetworkingContext::ensurePrivateBrowsingSession({ { }, { }, { }, { }, { }, { }, { sessionID, { }, { }, AllowsCellularAccess::Yes }});521 }522 523 518 void WebProcess::addWebsiteDataStore(WebsiteDataStoreParameters&& parameters) 524 519 { -
trunk/Source/WebKit/WebProcess/WebProcess.h
r224041 r224791 179 179 void setCacheModel(uint32_t); 180 180 181 void ensurePrivateBrowsingSession(PAL::SessionID);182 181 void ensureLegacyPrivateBrowsingSessionInNetworkProcess(); 183 182 void addWebsiteDataStore(WebsiteDataStoreParameters&&); -
trunk/Source/WebKit/WebProcess/WebProcess.messages.in
r223981 r224791 48 48 ClearCachedCredentials() 49 49 50 EnsurePrivateBrowsingSession(PAL::SessionID sessionID)51 50 AddWebsiteDataStore(struct WebKit::WebsiteDataStoreParameters websiteDataStoreParameters); 52 51 DestroySession(PAL::SessionID sessionID) -
trunk/Source/WebKitLegacy/mac/ChangeLog
r224787 r224791 1 2017-11-13 Alex Christensen <achristensen@webkit.org> 2 3 Merge NetworkProcess::EnsurePrivateBrowsingSession and NetworkProcess::AddWebsiteDataStore into one message type 4 https://bugs.webkit.org/show_bug.cgi?id=178751 5 6 Reviewed by Brady Eidson. 7 8 * WebCoreSupport/WebFrameNetworkingContext.mm: 9 (WebFrameNetworkingContext::ensurePrivateBrowsingSession): 10 1 11 2017-11-08 Keith Miller <keith_miller@apple.com> 2 12 -
trunk/Source/WebKitLegacy/mac/WebCoreSupport/WebFrameNetworkingContext.mm
r220887 r224791 47 47 { 48 48 ASSERT(isMainThread()); 49 NetworkStorageSession::ensure PrivateBrowsingSession(PAL::SessionID::legacyPrivateSessionID(), [[NSBundle mainBundle] bundleIdentifier]);49 NetworkStorageSession::ensureSession(PAL::SessionID::legacyPrivateSessionID(), [[NSBundle mainBundle] bundleIdentifier]); 50 50 return *NetworkStorageSession::storageSession(PAL::SessionID::legacyPrivateSessionID()); 51 51 } -
trunk/Source/WebKitLegacy/win/WebCoreSupport/WebFrameNetworkingContext.cpp
r220857 r224791 82 82 base = identifierBase(); 83 83 84 NetworkStorageSession::ensure PrivateBrowsingSession(PAL::SessionID::legacyPrivateSessionID(), base);84 NetworkStorageSession::ensureSession(PAL::SessionID::legacyPrivateSessionID(), base); 85 85 86 86 #endif
Note:
See TracChangeset
for help on using the changeset viewer.