286697d75b59681f6c9da12461f8b51ba6473bf9
[WebKit-https.git] / Source / WebKit / UIProcess / ApplicationStateTracker.mm
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "ApplicationStateTracker.h"
28
29 #if PLATFORM(IOS)
30
31 #import "AssertionServicesSPI.h"
32 #import "SandboxUtilities.h"
33 #import "UIKitSPI.h"
34 #import <wtf/ObjcRuntimeExtras.h>
35 #import <wtf/spi/cocoa/SecuritySPI.h>
36
37 @interface UIWindow (WKDetails)
38 - (BOOL)_isHostedInAnotherProcess;
39 @end
40
41 namespace WebKit {
42
43
44 enum class ApplicationType {
45     Application,
46     ViewService,
47     Extension,
48 };
49
50 static ApplicationType applicationType(UIWindow *window)
51 {
52     ASSERT(window);
53
54     if (_UIApplicationIsExtension())
55         return ApplicationType::Extension;
56
57     if (processHasEntitlement(@"com.apple.UIKit.vends-view-services") && window._isHostedInAnotherProcess)
58         return ApplicationType::ViewService;
59
60     return ApplicationType::Application;
61 }
62
63 static bool isBackgroundState(BKSApplicationState state)
64 {
65     switch (state) {
66     case BKSApplicationStateBackgroundRunning:
67     case BKSApplicationStateBackgroundTaskSuspended:
68         return true;
69
70     default:
71         return false;
72     }
73 }
74
75 ApplicationStateTracker::ApplicationStateTracker(UIView *view, SEL didEnterBackgroundSelector, SEL didCreateWindowContextSelector, SEL didFinishSnapshottingAfterEnteringBackgroundSelector, SEL willEnterForegroundSelector)
76     : m_view(view)
77     , m_didEnterBackgroundSelector(didEnterBackgroundSelector)
78     , m_didCreateWindowContextSelector(didCreateWindowContextSelector)
79     , m_didFinishSnapshottingAfterEnteringBackgroundSelector(didFinishSnapshottingAfterEnteringBackgroundSelector)
80     , m_willEnterForegroundSelector(willEnterForegroundSelector)
81     , m_isInBackground(true)
82     , m_weakPtrFactory(this)
83     , m_didEnterBackgroundObserver(nullptr)
84     , m_didCreateWindowContextObserver(nullptr)
85     , m_didFinishSnapshottingAfterEnteringBackgroundObserver(nullptr)
86     , m_willEnterForegroundObserver(nullptr)
87 {
88     ASSERT([m_view.get() respondsToSelector:m_didEnterBackgroundSelector]);
89     ASSERT([m_view.get() respondsToSelector:m_didCreateWindowContextSelector]);
90     ASSERT([m_view.get() respondsToSelector:m_didFinishSnapshottingAfterEnteringBackgroundSelector]);
91     ASSERT([m_view.get() respondsToSelector:m_willEnterForegroundSelector]);
92
93     UIWindow *window = [m_view.get() window];
94     ASSERT(window);
95
96     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
97     auto weakThis = m_weakPtrFactory.createWeakPtr();
98     m_didCreateWindowContextObserver = [notificationCenter addObserverForName:@"_UIWindowDidCreateWindowContextNotification" object:window queue:nil usingBlock:[weakThis](NSNotification *) {
99         auto applicationStateTracker = weakThis.get();
100         if (!applicationStateTracker)
101             return;
102         applicationStateTracker->applicationDidCreateWindowContext();
103     }];
104
105     m_didFinishSnapshottingAfterEnteringBackgroundObserver = [notificationCenter addObserverForName:@"_UIWindowWillDestroyWindowContextNotification" object:window queue:nil usingBlock:[weakThis](NSNotification *) {
106         auto applicationStateTracker = weakThis.get();
107         if (!applicationStateTracker)
108             return;
109         applicationStateTracker->applicationDidFinishSnapshottingAfterEnteringBackground();
110     }];
111
112     switch (applicationType(window)) {
113     case ApplicationType::Application: {
114         UIApplication *application = [UIApplication sharedApplication];
115
116         m_isInBackground = application.applicationState == UIApplicationStateBackground;
117
118         m_didEnterBackgroundObserver = [notificationCenter addObserverForName:UIApplicationDidEnterBackgroundNotification object:application queue:nil usingBlock:[this](NSNotification *) {
119             applicationDidEnterBackground();
120         }];
121
122         m_willEnterForegroundObserver = [notificationCenter addObserverForName:UIApplicationWillEnterForegroundNotification object:application queue:nil usingBlock:[this](NSNotification *) {
123             applicationWillEnterForeground();
124         }];
125         break;
126     }
127
128     case ApplicationType::ViewService: {
129         UIViewController *serviceViewController = nil;
130
131         for (UIView *view = m_view.get().get(); view; view = view.superview) {
132             UIViewController *viewController = [UIViewController viewControllerForView:view];
133
134             if (viewController._hostProcessIdentifier) {
135                 serviceViewController = viewController;
136                 break;
137             }
138         }
139
140         ASSERT(serviceViewController);
141
142         pid_t applicationPID = serviceViewController._hostProcessIdentifier;
143         ASSERT(applicationPID);
144
145         auto applicationStateMonitor = adoptNS([[BKSApplicationStateMonitor alloc] init]);
146         m_isInBackground = isBackgroundState([applicationStateMonitor mostElevatedApplicationStateForPID:applicationPID]);
147         [applicationStateMonitor invalidate];
148
149         m_didEnterBackgroundObserver = [notificationCenter addObserverForName:@"_UIViewServiceHostDidEnterBackgroundNotification" object:serviceViewController queue:nil usingBlock:[this](NSNotification *) {
150             applicationDidEnterBackground();
151         }];
152         m_willEnterForegroundObserver = [notificationCenter addObserverForName:@"_UIViewServiceHostWillEnterForegroundNotification" object:serviceViewController queue:nil usingBlock:[this](NSNotification *) {
153             applicationWillEnterForeground();
154         }];
155
156         break;
157     }
158
159     case ApplicationType::Extension: {
160         m_applicationStateMonitor = adoptNS([[BKSApplicationStateMonitor alloc] init]);
161
162         m_isInBackground = isBackgroundState([m_applicationStateMonitor mostElevatedApplicationStateForPID:getpid()]);
163
164         [m_applicationStateMonitor setHandler:[weakThis](NSDictionary *userInfo) {
165             pid_t pid = [userInfo[BKSApplicationStateProcessIDKey] integerValue];
166             if (pid != getpid())
167                 return;
168
169             BKSApplicationState newState = (BKSApplicationState)[userInfo[BKSApplicationStateMostElevatedStateForProcessIDKey] unsignedIntValue];
170             bool newInBackground = isBackgroundState(newState);
171
172             dispatch_async(dispatch_get_main_queue(), [weakThis, newInBackground] {
173                 auto applicationStateTracker = weakThis.get();
174                 if (!applicationStateTracker)
175                     return;
176
177                 if (!applicationStateTracker->m_isInBackground && newInBackground)
178                     applicationStateTracker->applicationDidEnterBackground();
179                 else if (applicationStateTracker->m_isInBackground && !newInBackground)
180                     applicationStateTracker->applicationWillEnterForeground();
181             });
182         }];
183     }
184     }
185 }
186
187 ApplicationStateTracker::~ApplicationStateTracker()
188 {
189     if (m_applicationStateMonitor) {
190         [m_applicationStateMonitor invalidate];
191         return;
192     }
193
194     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
195     [notificationCenter removeObserver:m_didEnterBackgroundObserver];
196     [notificationCenter removeObserver:m_didCreateWindowContextObserver];
197     [notificationCenter removeObserver:m_didFinishSnapshottingAfterEnteringBackgroundObserver];
198     [notificationCenter removeObserver:m_willEnterForegroundObserver];
199 }
200
201 void ApplicationStateTracker::applicationDidEnterBackground()
202 {
203     m_isInBackground = true;
204
205     if (auto view = m_view.get())
206         wtfObjcMsgSend<void>(view.get(), m_didEnterBackgroundSelector);
207 }
208
209 void ApplicationStateTracker::applicationDidCreateWindowContext()
210 {
211     if (auto view = m_view.get())
212         wtfObjcMsgSend<void>(view.get(), m_didCreateWindowContextSelector);
213 }
214
215 void ApplicationStateTracker::applicationDidFinishSnapshottingAfterEnteringBackground()
216 {
217     if (auto view = m_view.get())
218         wtfObjcMsgSend<void>(view.get(), m_didFinishSnapshottingAfterEnteringBackgroundSelector);
219 }
220
221 void ApplicationStateTracker::applicationWillEnterForeground()
222 {
223     m_isInBackground = false;
224
225     if (auto view = m_view.get())
226         wtfObjcMsgSend<void>(view.get(), m_willEnterForegroundSelector);
227 }
228
229 }
230
231 #endif