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