xref: /haiku/src/apps/webpositive/BrowserApp.cpp (revision 3b07762c548ec4016dea480d1061577cd15ec614)
1 /*
2  * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com>
3  * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *	notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *	notice, this list of conditions and the following disclaimer in the
14  *	documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * 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 #include "BrowserApp.h"
30 
31 #include <AboutWindow.h>
32 #include <Alert.h>
33 #include <Autolock.h>
34 #include <Catalog.h>
35 #include <Directory.h>
36 #include <Entry.h>
37 #include <FindDirectory.h>
38 #include <Locale.h>
39 #include <Path.h>
40 #include <Screen.h>
41 #include <UrlContext.h>
42 #include <debugger.h>
43 
44 #include <stdio.h>
45 
46 #include "BrowserWindow.h"
47 #include "BrowsingHistory.h"
48 #include "DownloadWindow.h"
49 #include "SettingsMessage.h"
50 #include "SettingsWindow.h"
51 #include "NetworkCookieJar.h"
52 #include "WebKitInfo.h"
53 #include "WebPage.h"
54 #include "WebSettings.h"
55 #include "WebView.h"
56 
57 
58 #undef B_TRANSLATION_CONTEXT
59 #define B_TRANSLATION_CONTEXT "WebPositive"
60 
61 const char* kApplicationSignature = "application/x-vnd.Haiku-WebPositive";
62 const char* kApplicationName = B_TRANSLATE_SYSTEM_NAME("WebPositive");
63 static const uint32 PRELOAD_BROWSING_HISTORY = 'plbh';
64 
65 #define ENABLE_NATIVE_COOKIES 1
66 
67 
68 BrowserApp::BrowserApp()
69 	:
70 	BApplication(kApplicationSignature),
71 	fWindowCount(0),
72 	fLastWindowFrame(50, 50, 950, 750),
73 	fLaunchRefsMessage(0),
74 	fInitialized(false),
75 	fSettings(NULL),
76 	fCookies(NULL),
77 	fContext(NULL),
78 	fDownloadWindow(NULL),
79 	fSettingsWindow(NULL)
80 {
81 #if ENABLE_NATIVE_COOKIES
82 	BString cookieStorePath = kApplicationName;
83 	cookieStorePath << "/Cookies";
84 	fCookies = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
85 		cookieStorePath.String());
86 	BMessage cookieArchive = fCookies->GetValue("cookies", cookieArchive);
87 	fContext = new BUrlContext();
88 	fContext->SetCookieJar(BNetworkCookieJar(&cookieArchive));
89 #endif
90 }
91 
92 
93 BrowserApp::~BrowserApp()
94 {
95 	delete fLaunchRefsMessage;
96 	delete fSettings;
97 	delete fCookies;
98 	delete fContext;
99 }
100 
101 
102 void
103 BrowserApp::AboutRequested()
104 {
105 	BAboutWindow* window = new BAboutWindow(kApplicationName,
106 		kApplicationSignature);
107 
108 	// create the about window
109 
110 	const char* authors[] = {
111 		"Andrea Anzani",
112 		"Stephan Aßmus",
113 		"Alexandre Deckner",
114 		"Rene Gollent",
115 		"Ryan Leavengood",
116 		"Michael Lotz",
117 		"Maxime Simon",
118 		NULL
119 	};
120 
121 	BString aboutText("");
122 	aboutText << "HaikuWebKit " << WebKitInfo::HaikuWebKitVersion();
123 	aboutText << "\nWebKit " << WebKitInfo::WebKitVersion();
124 
125 	window->AddCopyright(2007, "Haiku, Inc.");
126 	window->AddAuthors(authors);
127 	window->AddExtraInfo(aboutText.String());
128 
129 	window->Show();
130 }
131 
132 
133 void
134 BrowserApp::ArgvReceived(int32 argc, char** argv)
135 {
136 	BMessage message(B_REFS_RECEIVED);
137 	for (int i = 1; i < argc; i++) {
138 		if (strcmp("-f", argv[i]) == 0
139 			|| strcmp("--fullscreen", argv[i]) == 0) {
140 			message.AddBool("fullscreen", true);
141 			continue;
142 		}
143 		const char* url = argv[i];
144 		BEntry entry(argv[i], true);
145 		BPath path;
146 		if (entry.Exists() && entry.GetPath(&path) == B_OK)
147 			url = path.Path();
148 		message.AddString("url", url);
149 	}
150 	// Upon program launch, it will buffer a copy of the message, since
151 	// ArgReceived() is called before ReadyToRun().
152 	RefsReceived(&message);
153 }
154 
155 
156 void
157 BrowserApp::ReadyToRun()
158 {
159 	// Since we will essentially run the GUI...
160 	set_thread_priority(Thread(), B_DISPLAY_PRIORITY);
161 
162 	BWebPage::InitializeOnce();
163 	BWebPage::SetCacheModel(B_WEBKIT_CACHE_MODEL_WEB_BROWSER);
164 
165 	BPath path;
166 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK
167 		&& path.Append(kApplicationName) == B_OK
168 		&& create_directory(path.Path(), 0777) == B_OK) {
169 
170 		BWebSettings::SetPersistentStoragePath(path.Path());
171 	}
172 
173 	BString mainSettingsPath(kApplicationName);
174 	mainSettingsPath << "/Application";
175 	fSettings = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
176 		mainSettingsPath.String());
177 
178 	fLastWindowFrame = fSettings->GetValue("window frame", fLastWindowFrame);
179 	BRect defaultDownloadWindowFrame(-10, -10, 365, 265);
180 	BRect downloadWindowFrame = fSettings->GetValue("downloads window frame",
181 		defaultDownloadWindowFrame);
182 	BRect settingsWindowFrame = fSettings->GetValue("settings window frame",
183 		BRect());
184 	bool showDownloads = fSettings->GetValue("show downloads", false);
185 
186 	fDownloadWindow = new DownloadWindow(downloadWindowFrame, showDownloads,
187 		fSettings);
188 	if (downloadWindowFrame == defaultDownloadWindowFrame) {
189 		// Initially put download window in lower right of screen.
190 		BRect screenFrame = BScreen().Frame();
191 		BMessage decoratorSettings;
192 		fDownloadWindow->GetDecoratorSettings(&decoratorSettings);
193 		float borderWidth = 0;
194 		if (decoratorSettings.FindFloat("border width", &borderWidth) != B_OK)
195 			borderWidth = 5;
196 		fDownloadWindow->MoveTo(screenFrame.Width()
197 			- fDownloadWindow->Frame().Width() - borderWidth,
198 			screenFrame.Height() - fDownloadWindow->Frame().Height()
199 			- borderWidth);
200 	}
201 	fSettingsWindow = new SettingsWindow(settingsWindowFrame, fSettings);
202 
203 	BWebPage::SetDownloadListener(BMessenger(fDownloadWindow));
204 
205 	fInitialized = true;
206 
207 	int32 pagesCreated = 0;
208 	bool fullscreen = false;
209 	if (fLaunchRefsMessage) {
210 		_RefsReceived(fLaunchRefsMessage, &pagesCreated, &fullscreen);
211 		delete fLaunchRefsMessage;
212 		fLaunchRefsMessage = NULL;
213 	}
214 	if (pagesCreated == 0)
215 		_CreateNewWindow("", fullscreen);
216 
217 	PostMessage(PRELOAD_BROWSING_HISTORY);
218 }
219 
220 
221 void
222 BrowserApp::MessageReceived(BMessage* message)
223 {
224 	switch (message->what) {
225 	case PRELOAD_BROWSING_HISTORY:
226 		// Accessing the default instance will load the history from disk.
227 		BrowsingHistory::DefaultInstance();
228 		break;
229 	case B_SILENT_RELAUNCH:
230 		_CreateNewPage("");
231 		break;
232 	case NEW_WINDOW: {
233 		BString url;
234 		if (message->FindString("url", &url) != B_OK)
235 			break;
236 		_CreateNewWindow(url);
237 		break;
238 	}
239 	case NEW_TAB: {
240 		BrowserWindow* window;
241 		if (message->FindPointer("window",
242 			reinterpret_cast<void**>(&window)) != B_OK)
243 			break;
244 		BString url;
245 		message->FindString("url", &url);
246 		bool select = false;
247 		message->FindBool("select", &select);
248 		_CreateNewTab(window, url, select);
249 		break;
250 	}
251 	case WINDOW_OPENED:
252 		fWindowCount++;
253 		fDownloadWindow->SetMinimizeOnClose(false);
254 		break;
255 	case WINDOW_CLOSED:
256 		fWindowCount--;
257 		message->FindRect("window frame", &fLastWindowFrame);
258 		if (fWindowCount <= 0)
259 			PostMessage(B_QUIT_REQUESTED);
260 		break;
261 
262 	case SHOW_DOWNLOAD_WINDOW:
263 		_ShowWindow(message, fDownloadWindow);
264 		break;
265 	case SHOW_SETTINGS_WINDOW:
266 		_ShowWindow(message, fSettingsWindow);
267 		break;
268 
269 	default:
270 		BApplication::MessageReceived(message);
271 		break;
272 	}
273 }
274 
275 
276 void
277 BrowserApp::RefsReceived(BMessage* message)
278 {
279 	if (!fInitialized) {
280 		delete fLaunchRefsMessage;
281 		fLaunchRefsMessage = new BMessage(*message);
282 		return;
283 	}
284 
285 	_RefsReceived(message);
286 }
287 
288 
289 bool
290 BrowserApp::QuitRequested()
291 {
292 	if (fDownloadWindow->DownloadsInProgress()) {
293 		BAlert* alert = new BAlert(B_TRANSLATE("Downloads in progress"),
294 			B_TRANSLATE("There are still downloads in progress, do you really "
295 			"want to quit WebPositive now?"), B_TRANSLATE("Quit"),
296 			B_TRANSLATE("Continue downloads"));
297 		int32 choice = alert->Go();
298 		if (choice == 1) {
299 			if (fWindowCount == 0) {
300 				if (fDownloadWindow->Lock()) {
301 					fDownloadWindow->SetWorkspaces(1 << current_workspace());
302 					if (fDownloadWindow->IsHidden())
303 						fDownloadWindow->Show();
304 					else
305 						fDownloadWindow->Activate();
306 					fDownloadWindow->SetMinimizeOnClose(true);
307 					fDownloadWindow->Unlock();
308 					return false;
309 				}
310 			} else
311 				return false;
312 		}
313 	}
314 
315 	for (int i = 0; BWindow* window = WindowAt(i); i++) {
316 		BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
317 		if (!webWindow)
318 			continue;
319 		if (!webWindow->Lock())
320 			continue;
321 		if (webWindow->QuitRequested()) {
322 			fLastWindowFrame = webWindow->WindowFrame();
323 			webWindow->Quit();
324 			i--;
325 		} else {
326 			webWindow->Unlock();
327 			return false;
328 		}
329 	}
330 
331 	BWebPage::ShutdownOnce();
332 
333 	fSettings->SetValue("window frame", fLastWindowFrame);
334 	if (fDownloadWindow->Lock()) {
335 		fSettings->SetValue("downloads window frame", fDownloadWindow->Frame());
336 		fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden());
337 		fDownloadWindow->Unlock();
338 	}
339 	if (fSettingsWindow->Lock()) {
340 		fSettings->SetValue("settings window frame", fSettingsWindow->Frame());
341 		fSettingsWindow->Unlock();
342 	}
343 
344 	BMessage cookieArchive;
345 	BNetworkCookieJar& cookieJar = fContext->GetCookieJar();
346 	if (cookieJar.Archive(&cookieArchive) == B_OK)
347 		fCookies->SetValue("cookies", cookieArchive);
348 
349 	return true;
350 }
351 
352 
353 void
354 BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated,
355 	bool* _fullscreen)
356 {
357 	int32 pagesCreated = 0;
358 
359 	BrowserWindow* window = NULL;
360 	if (message->FindPointer("window", (void**)&window) != B_OK)
361 		window = NULL;
362 
363 	bool fullscreen;
364 	if (message->FindBool("fullscreen", &fullscreen) != B_OK)
365 		fullscreen = false;
366 
367 	entry_ref ref;
368 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
369 		BEntry entry(&ref, true);
370 		if (!entry.Exists())
371 			continue;
372 		BPath path;
373 		if (entry.GetPath(&path) != B_OK)
374 			continue;
375 		BString url;
376 		url << path.Path();
377 		window = _CreateNewPage(url, window, fullscreen, pagesCreated == 0);
378 		pagesCreated++;
379 	}
380 
381 	BString url;
382 	for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) {
383 		window = _CreateNewPage(url, window, fullscreen, pagesCreated == 0);
384 		pagesCreated++;
385 	}
386 
387 	if (_pagesCreated != NULL)
388 		*_pagesCreated = pagesCreated;
389 	if (_fullscreen != NULL)
390 		*_fullscreen = fullscreen;
391 }
392 
393 
394 BrowserWindow*
395 BrowserApp::_CreateNewPage(const BString& url, BrowserWindow* webWindow,
396 	bool fullscreen, bool useBlankTab)
397 {
398 	// Let's first see if we must target a specific window...
399 	if (webWindow && webWindow->Lock()) {
400 		if (useBlankTab && webWindow->IsBlankTab()) {
401 			if (url.Length() != 0)
402 				webWindow->CurrentWebView()->LoadURL(url);
403 		} else
404 			webWindow->CreateNewTab(url, true);
405 		webWindow->Activate();
406 		webWindow->CurrentWebView()->MakeFocus(true);
407 		webWindow->Unlock();
408 		return webWindow;
409 	}
410 
411 	// Otherwise, try to find one in the current workspace
412 	uint32 workspace = 1 << current_workspace();
413 
414 	bool loadedInWindowOnCurrentWorkspace = false;
415 	for (int i = 0; BWindow* window = WindowAt(i); i++) {
416 		webWindow = dynamic_cast<BrowserWindow*>(window);
417 		if (!webWindow)
418 			continue;
419 
420 		if (webWindow->Lock()) {
421 			if (webWindow->Workspaces() & workspace) {
422 				if (useBlankTab && webWindow->IsBlankTab()) {
423 					if (url.Length() != 0)
424 						webWindow->CurrentWebView()->LoadURL(url);
425 				} else
426 					webWindow->CreateNewTab(url, true);
427 				webWindow->Activate();
428 				webWindow->CurrentWebView()->MakeFocus(true);
429 				loadedInWindowOnCurrentWorkspace = true;
430 			}
431 			webWindow->Unlock();
432 		}
433 		if (loadedInWindowOnCurrentWorkspace)
434 			return webWindow;
435 	}
436 
437 	// Finally, if no window is available, let's create one.
438 	return _CreateNewWindow(url, fullscreen);
439 }
440 
441 
442 BrowserWindow*
443 BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen)
444 {
445 	// Offset the window frame unless this is the first window created in the
446 	// session.
447 	if (fWindowCount > 0)
448 		fLastWindowFrame.OffsetBy(20, 20);
449 	if (!BScreen().Frame().Contains(fLastWindowFrame))
450 		fLastWindowFrame.OffsetTo(50, 50);
451 
452 	BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings,
453 		url, fContext);
454 	if (fullscreen)
455 		window->ToggleFullscreen();
456 	window->Show();
457 	return window;
458 }
459 
460 
461 void
462 BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url,
463 	bool select)
464 {
465 	if (!window->Lock())
466 		return;
467 	window->CreateNewTab(url, select);
468 	window->Unlock();
469 }
470 
471 
472 void
473 BrowserApp::_ShowWindow(const BMessage* message, BWindow* window)
474 {
475 	BAutolock _(window);
476 	uint32 workspaces;
477 	if (message->FindUInt32("workspaces", &workspaces) == B_OK)
478 		window->SetWorkspaces(workspaces);
479 	if (window->IsHidden())
480 		window->Show();
481 	else
482 		window->Activate();
483 }
484 
485 
486 // #pragma mark -
487 
488 
489 int
490 main(int, char**)
491 {
492 	try {
493 		new BrowserApp();
494 		be_app->Run();
495 		delete be_app;
496 	} catch (...) {
497 		debugger("Exception caught.");
498 	}
499 
500 	return 0;
501 }
502 
503