OLD | NEW |
| (Empty) |
1 /* | |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | |
3 * Copyright (C) 2006-2016 Eyeo GmbH | |
4 * | |
5 * Adblock Plus is free software: you can redistribute it and/or modify | |
6 * it under the terms of the GNU General Public License version 3 as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * Adblock Plus is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU General Public License | |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | |
16 */ | |
17 | |
18 (function() | |
19 { | |
20 // the safari object is missing in frames created from javascript: URLs. | |
21 // So we have to fallback to the safari object from the parent frame. | |
22 if (!("safari" in window)) | |
23 window.safari = window.parent.safari; | |
24 | |
25 | |
26 /* Intialization */ | |
27 | |
28 var beforeLoadEvent = document.createEvent("Event"); | |
29 beforeLoadEvent.initEvent("beforeload", false, true); | |
30 | |
31 // Decide if we should use the new content blocker API or not. (Note when the | |
32 // API is used Safari breaks the canLoad function, making it either throw an | |
33 // exception or return true when used.) | |
34 var usingContentBlockerAPI = true; | |
35 try | |
36 { | |
37 if (safari.self.tab.canLoad(beforeLoadEvent, | |
38 {category: "request", | |
39 payload: {type: "prefs.get", | |
40 key: "safariContentBlocker"}}) != tru
e) | |
41 usingContentBlockerAPI = false; | |
42 } | |
43 catch (e) | |
44 { | |
45 } | |
46 | |
47 var isTopLevel; | |
48 var isPrerendered; | |
49 var documentId; | |
50 function notifyFrameLoading() | |
51 { | |
52 isTopLevel = window == window.top; | |
53 isPrerendered = document.visibilityState == "prerender"; | |
54 documentId = Math.random().toString().substr(2); | |
55 | |
56 // Notify the background page that this frame is loading, generating | |
57 // ourselves a random documentId while we're at it. That way the background | |
58 // page can communicate with us reliably, despite limitations in Safari's | |
59 // extension API. | |
60 safari.self.tab.dispatchMessage("loading", { | |
61 url: window.location.href, | |
62 referrer: document.referrer, | |
63 isTopLevel: isTopLevel, | |
64 isPrerendered: isPrerendered, | |
65 documentId: documentId, | |
66 legacyAPISupported: "canLoad" in safari.self.tab && | |
67 "onbeforeload" in Element.prototype | |
68 }); | |
69 } | |
70 | |
71 // We must notify the background page when this page is first loadeding (now) | |
72 // but also when it is re-shown (if the user uses the back button to return to | |
73 // this page in the future). | |
74 notifyFrameLoading(); | |
75 window.addEventListener("pageshow", function(event) | |
76 { | |
77 if (event.persisted) | |
78 notifyFrameLoading(); | |
79 }); | |
80 | |
81 // Notify the background page when a prerendered page is displayed. That way | |
82 // the existing page of the tab can be replaced with this new one. | |
83 if (isTopLevel && isPrerendered) | |
84 { | |
85 var onVisibilitychange = function() | |
86 { | |
87 safari.self.tab.dispatchMessage("replaced", {documentId: documentId}); | |
88 document.removeEventListener("visibilitychange", onVisibilitychange); | |
89 }; | |
90 document.addEventListener("visibilitychange", onVisibilitychange); | |
91 } | |
92 | |
93 /* Web requests */ | |
94 | |
95 if (!usingContentBlockerAPI) | |
96 { | |
97 document.addEventListener("beforeload", function(event) | |
98 { | |
99 // we don't block non-HTTP requests anyway, so we can bail out | |
100 // without asking the background page. This is even necessary | |
101 // because passing large data (like a photo encoded as data: URL) | |
102 // to the background page, freezes Safari. | |
103 if (/^(?!https?:)[\w-]+:/.test(event.url)) | |
104 return; | |
105 | |
106 var type = "OTHER"; | |
107 var eventName = "error"; | |
108 | |
109 switch(event.target.localName) | |
110 { | |
111 case "frame": | |
112 case "iframe": | |
113 type = "SUBDOCUMENT"; | |
114 eventName = "load"; | |
115 break; | |
116 case "img": | |
117 case "input": | |
118 type = "IMAGE"; | |
119 break; | |
120 case "video": | |
121 case "audio": | |
122 case "source": | |
123 type = "MEDIA"; | |
124 break; | |
125 case "object": | |
126 case "embed": | |
127 type = "OBJECT"; | |
128 break; | |
129 case "script": | |
130 type = "SCRIPT"; | |
131 break; | |
132 case "link": | |
133 if (/\bstylesheet\b/i.test(event.target.rel)) | |
134 type = "STYLESHEET"; | |
135 break; | |
136 } | |
137 | |
138 if (!safari.self.tab.canLoad( | |
139 event, { | |
140 category: "webRequest", | |
141 url: event.url, | |
142 type: type, | |
143 documentId: documentId})) | |
144 { | |
145 event.preventDefault(); | |
146 | |
147 // Safari doesn't dispatch the expected events for elements that have | |
148 // been prevented from loading by having their "beforeload" event | |
149 // cancelled. That is a "load" event for blocked frames, and an "error" | |
150 // event for other blocked elements. We need to dispatch those events | |
151 // manually here to avoid breaking element collapsing and pages that | |
152 // rely on those events. | |
153 setTimeout(function() | |
154 { | |
155 var evt = document.createEvent("Event"); | |
156 evt.initEvent(eventName, false, false); | |
157 event.target.dispatchEvent(evt); | |
158 }); | |
159 } | |
160 }, true); | |
161 } | |
162 | |
163 | |
164 /* Context menus */ | |
165 | |
166 document.addEventListener("contextmenu", function(event) | |
167 { | |
168 var element = event.srcElement; | |
169 safari.self.tab.setContextMenuEventUserInfo(event, { | |
170 documentId: documentId, | |
171 tagName: element.localName | |
172 }); | |
173 }); | |
174 | |
175 | |
176 /* Background page */ | |
177 | |
178 ext.backgroundPage = { | |
179 sendMessage: function(message, responseCallback) | |
180 { | |
181 messageProxy.sendMessage(message, responseCallback, | |
182 {documentId: documentId}); | |
183 }, | |
184 sendMessageSync: function(message) | |
185 { | |
186 return safari.self.tab.canLoad( | |
187 beforeLoadEvent, | |
188 { | |
189 category: "request", | |
190 documentId: documentId, | |
191 payload: message | |
192 } | |
193 ); | |
194 } | |
195 }; | |
196 | |
197 | |
198 /* Message processing */ | |
199 | |
200 var messageProxy = new ext._MessageProxy(safari.self.tab); | |
201 | |
202 safari.self.addEventListener("message", function(event) | |
203 { | |
204 if (event.name == "requestDocumentId" && isTopLevel) | |
205 { | |
206 safari.self.tab.dispatchMessage("documentId", { | |
207 pageId: event.message.pageId, | |
208 documentId: documentId | |
209 }); | |
210 } | |
211 else if (event.message.targetDocuments.indexOf(documentId) != -1) | |
212 { | |
213 switch (event.name) | |
214 { | |
215 case "request": | |
216 messageProxy.handleRequest(event.message, {}); | |
217 break; | |
218 case "response": | |
219 messageProxy.handleResponse(event.message); | |
220 break; | |
221 } | |
222 } | |
223 }); | |
224 | |
225 | |
226 /* Detecting extension reload/disable/uninstall (not supported on Safari) */ | |
227 | |
228 ext.onExtensionUnloaded = { | |
229 addListener: function() {}, | |
230 removeListener: function() {} | |
231 }; | |
232 })(); | |
OLD | NEW |