1/*
2 * Copyright (C) 2011 Google 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 are
6 * met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @constructor
31 * @extends {WebInspector.Object}
32 */
33WebInspector.ApplicationCacheModel = function()
34{
35    ApplicationCacheAgent.enable();
36    InspectorBackend.registerApplicationCacheDispatcher(new WebInspector.ApplicationCacheDispatcher(this));
37
38    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
39    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this._frameDetached, this);
40
41    this._statuses = {};
42    this._manifestURLsByFrame = {};
43
44    this._mainFrameNavigated();
45
46    this._onLine = true;
47}
48
49WebInspector.ApplicationCacheModel.EventTypes = {
50    FrameManifestStatusUpdated: "FrameManifestStatusUpdated",
51    FrameManifestAdded: "FrameManifestAdded",
52    FrameManifestRemoved: "FrameManifestRemoved",
53    NetworkStateChanged: "NetworkStateChanged"
54}
55
56WebInspector.ApplicationCacheModel.prototype = {
57    _frameNavigated: function(event)
58    {
59        var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
60        if (frame.isMainFrame()) {
61            this._mainFrameNavigated();
62            return;
63        }
64
65        ApplicationCacheAgent.getManifestForFrame(frame.id, this._manifestForFrameLoaded.bind(this, frame.id));
66    },
67
68    /**
69     * @param {!WebInspector.Event} event
70     */
71    _frameDetached: function(event)
72    {
73        var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
74        this._frameManifestRemoved(frame.id);
75    },
76
77    _mainFrameNavigated: function()
78    {
79        ApplicationCacheAgent.getFramesWithManifests(this._framesWithManifestsLoaded.bind(this));
80    },
81
82    /**
83     * @param {string} frameId
84     * @param {?Protocol.Error} error
85     * @param {string} manifestURL
86     */
87    _manifestForFrameLoaded: function(frameId, error, manifestURL)
88    {
89        if (error) {
90            console.error(error);
91            return;
92        }
93
94        if (!manifestURL)
95            this._frameManifestRemoved(frameId);
96    },
97
98    /**
99     * @param {?Protocol.Error} error
100     * @param {!Array.<!ApplicationCacheAgent.FrameWithManifest>} framesWithManifests
101     */
102    _framesWithManifestsLoaded: function(error, framesWithManifests)
103    {
104        if (error) {
105            console.error(error);
106            return;
107        }
108
109        for (var i = 0; i < framesWithManifests.length; ++i)
110            this._frameManifestUpdated(framesWithManifests[i].frameId, framesWithManifests[i].manifestURL, framesWithManifests[i].status);
111    },
112
113    /**
114     * @param {string} frameId
115     * @param {string} manifestURL
116     * @param {number} status
117     */
118    _frameManifestUpdated: function(frameId, manifestURL, status)
119    {
120        if (status === applicationCache.UNCACHED) {
121            this._frameManifestRemoved(frameId);
122            return;
123        }
124
125        if (!manifestURL)
126            return;
127
128        if (this._manifestURLsByFrame[frameId] && manifestURL !== this._manifestURLsByFrame[frameId])
129            this._frameManifestRemoved(frameId);
130
131        var statusChanged = this._statuses[frameId] !== status;
132        this._statuses[frameId] = status;
133
134        if (!this._manifestURLsByFrame[frameId]) {
135            this._manifestURLsByFrame[frameId] = manifestURL;
136            this.dispatchEventToListeners(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestAdded, frameId);
137        }
138
139        if (statusChanged)
140            this.dispatchEventToListeners(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestStatusUpdated, frameId);
141    },
142
143    /**
144     * @param {string} frameId
145     */
146    _frameManifestRemoved: function(frameId)
147    {
148        if (!this._manifestURLsByFrame[frameId])
149            return;
150
151        var manifestURL = this._manifestURLsByFrame[frameId];
152        delete this._manifestURLsByFrame[frameId];
153        delete this._statuses[frameId];
154
155        this.dispatchEventToListeners(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestRemoved, frameId);
156    },
157
158    /**
159     * @param {string} frameId
160     * @return {string}
161     */
162    frameManifestURL: function(frameId)
163    {
164        return this._manifestURLsByFrame[frameId] || "";
165    },
166
167    /**
168     * @param {string} frameId
169     * @return {number}
170     */
171    frameManifestStatus: function(frameId)
172    {
173        return this._statuses[frameId] || applicationCache.UNCACHED;
174    },
175
176    /**
177     * @return {boolean}
178     */
179    get onLine()
180    {
181        return this._onLine;
182    },
183
184    /**
185     * @param {string} frameId
186     * @param {string} manifestURL
187     * @param {number} status
188     */
189    _statusUpdated: function(frameId, manifestURL, status)
190    {
191        this._frameManifestUpdated(frameId, manifestURL, status);
192    },
193
194    /**
195     * @param {string} frameId
196     * @param {function(?ApplicationCacheAgent.ApplicationCache)} callback
197     */
198    requestApplicationCache: function(frameId, callback)
199    {
200        /**
201         * @param {?Protocol.Error} error
202         * @param {!ApplicationCacheAgent.ApplicationCache} applicationCache
203         */
204        function callbackWrapper(error, applicationCache)
205        {
206            if (error) {
207                console.error(error);
208                callback(null);
209                return;
210            }
211
212            callback(applicationCache);
213        }
214
215        ApplicationCacheAgent.getApplicationCacheForFrame(frameId, callbackWrapper.bind(this));
216    },
217
218    /**
219     * @param {boolean} isNowOnline
220     */
221    _networkStateUpdated: function(isNowOnline)
222    {
223        this._onLine = isNowOnline;
224        this.dispatchEventToListeners(WebInspector.ApplicationCacheModel.EventTypes.NetworkStateChanged, isNowOnline);
225    },
226
227    __proto__: WebInspector.Object.prototype
228}
229
230/**
231 * @constructor
232 * @implements {ApplicationCacheAgent.Dispatcher}
233 */
234WebInspector.ApplicationCacheDispatcher = function(applicationCacheModel)
235{
236    this._applicationCacheModel = applicationCacheModel;
237}
238
239WebInspector.ApplicationCacheDispatcher.prototype = {
240    /**
241     * @param {string} frameId
242     * @param {string} manifestURL
243     * @param {number} status
244     */
245    applicationCacheStatusUpdated: function(frameId, manifestURL, status)
246    {
247        this._applicationCacheModel._statusUpdated(frameId, manifestURL, status);
248    },
249
250    /**
251     * @param {boolean} isNowOnline
252     */
253    networkStateUpdated: function(isNowOnline)
254    {
255        this._applicationCacheModel._networkStateUpdated(isNowOnline);
256    }
257}
258