xref: /haiku/src/apps/webpositive/BrowserApp.cpp (revision 776c58b2b56d8bcf33638a2ecb6c697f95a1cbf3)
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 <debugger.h>
42 
43 #include <stdio.h>
44 
45 #include "BrowserWindow.h"
46 #include "BrowsingHistory.h"
47 #include "DownloadWindow.h"
48 #include "SettingsMessage.h"
49 #include "SettingsWindow.h"
50 #include "svn_revision.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 0
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 	fCookieJar(NULL),
78 	fDownloadWindow(NULL),
79 	fSettingsWindow(NULL),
80 	fAboutWindow(NULL)
81 {
82 }
83 
84 
85 BrowserApp::~BrowserApp()
86 {
87 	delete fLaunchRefsMessage;
88 	delete fSettings;
89 	delete fCookies;
90 	delete fCookieJar;
91 
92 	if (fAboutWindow != NULL && fAboutWindow->Lock())
93 		fAboutWindow->Quit();
94 }
95 
96 
97 void
98 BrowserApp::AboutRequested()
99 {
100 	if (fAboutWindow == NULL) {
101 		// create the about window
102 
103 		const char* authors[] = {
104 			"Andrea Anzani",
105 			"Stephan Aßmus",
106 			"Alexandre Deckner",
107 			"Rene Gollent",
108 			"Ryan Leavengood",
109 			"Michael Lotz",
110 			"Maxime Simon",
111 			NULL
112 		};
113 
114 		BString aboutText("");
115 		aboutText << "HaikuWebKit " << WebKitInfo::HaikuWebKitVersion();
116 		aboutText << " (" << WebKitInfo::HaikuWebKitRevision() << ")";
117 		aboutText << "\nWebKit " << WebKitInfo::WebKitVersion();
118 		aboutText << " (" << WebKitInfo::WebKitRevision() << ")";
119 
120 		fAboutWindow = new BAboutWindow(kApplicationName,
121 			kApplicationSignature);
122 		fAboutWindow->AddCopyright(2007, "Haiku, Inc.");
123 		fAboutWindow->AddAuthors(authors);
124 		fAboutWindow->AddExtraInfo(aboutText.String());
125 		fAboutWindow->Show();
126 	} else if (fAboutWindow->IsHidden())
127 		fAboutWindow->Show();
128 	else
129 		fAboutWindow->Activate();
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 #if ENABLE_NATIVE_COOKIES
178 	mainSettingsPath = kApplicationName;
179 	mainSettingsPath << "/Cookies";
180 	fCookies = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
181 		mainSettingsPath.String());
182 	BMessage cookieArchive;
183 	cookieArchive = fCookies->GetValue("cookies", cookieArchive);
184 	fCookieJar = new BNetworkCookieJar(cookieArchive);
185 	BWebPage::SetCookieJar(fCookieJar);
186 #endif
187 
188 	fLastWindowFrame = fSettings->GetValue("window frame", fLastWindowFrame);
189 	BRect defaultDownloadWindowFrame(-10, -10, 365, 265);
190 	BRect downloadWindowFrame = fSettings->GetValue("downloads window frame",
191 		defaultDownloadWindowFrame);
192 	BRect settingsWindowFrame = fSettings->GetValue("settings window frame",
193 		BRect());
194 	bool showDownloads = fSettings->GetValue("show downloads", false);
195 
196 	fDownloadWindow = new DownloadWindow(downloadWindowFrame, showDownloads,
197 		fSettings);
198 	if (downloadWindowFrame == defaultDownloadWindowFrame) {
199 		// Initially put download window in lower right of screen.
200 		BRect screenFrame = BScreen().Frame();
201 		BMessage decoratorSettings;
202 		fDownloadWindow->GetDecoratorSettings(&decoratorSettings);
203 		float borderWidth = 0;
204 		if (decoratorSettings.FindFloat("border width", &borderWidth) != B_OK)
205 			borderWidth = 5;
206 		fDownloadWindow->MoveTo(screenFrame.Width()
207 			- fDownloadWindow->Frame().Width() - borderWidth,
208 			screenFrame.Height() - fDownloadWindow->Frame().Height()
209 			- borderWidth);
210 	}
211 	fSettingsWindow = new SettingsWindow(settingsWindowFrame, fSettings);
212 
213 	BWebPage::SetDownloadListener(BMessenger(fDownloadWindow));
214 
215 	fInitialized = true;
216 
217 	int32 pagesCreated = 0;
218 	bool fullscreen = false;
219 	if (fLaunchRefsMessage) {
220 		_RefsReceived(fLaunchRefsMessage, &pagesCreated, &fullscreen);
221 		delete fLaunchRefsMessage;
222 		fLaunchRefsMessage = NULL;
223 	}
224 	if (pagesCreated == 0)
225 		_CreateNewWindow("", fullscreen);
226 
227 	PostMessage(PRELOAD_BROWSING_HISTORY);
228 }
229 
230 
231 void
232 BrowserApp::MessageReceived(BMessage* message)
233 {
234 	switch (message->what) {
235 	case PRELOAD_BROWSING_HISTORY:
236 		// Accessing the default instance will load the history from disk.
237 		BrowsingHistory::DefaultInstance();
238 		break;
239 	case B_SILENT_RELAUNCH:
240 		_CreateNewPage("");
241 		break;
242 	case NEW_WINDOW: {
243 		BString url;
244 		if (message->FindString("url", &url) != B_OK)
245 			break;
246 		_CreateNewWindow(url);
247 		break;
248 	}
249 	case NEW_TAB: {
250 		BrowserWindow* window;
251 		if (message->FindPointer("window",
252 			reinterpret_cast<void**>(&window)) != B_OK)
253 			break;
254 		BString url;
255 		message->FindString("url", &url);
256 		bool select = false;
257 		message->FindBool("select", &select);
258 		_CreateNewTab(window, url, select);
259 		break;
260 	}
261 	case WINDOW_OPENED:
262 		fWindowCount++;
263 		fDownloadWindow->SetMinimizeOnClose(false);
264 		break;
265 	case WINDOW_CLOSED:
266 		fWindowCount--;
267 		message->FindRect("window frame", &fLastWindowFrame);
268 		if (fWindowCount <= 0)
269 			PostMessage(B_QUIT_REQUESTED);
270 		break;
271 
272 	case SHOW_DOWNLOAD_WINDOW:
273 		_ShowWindow(message, fDownloadWindow);
274 		break;
275 	case SHOW_SETTINGS_WINDOW:
276 		_ShowWindow(message, fSettingsWindow);
277 		break;
278 
279 	default:
280 		BApplication::MessageReceived(message);
281 		break;
282 	}
283 }
284 
285 
286 void
287 BrowserApp::RefsReceived(BMessage* message)
288 {
289 	if (!fInitialized) {
290 		delete fLaunchRefsMessage;
291 		fLaunchRefsMessage = new BMessage(*message);
292 		return;
293 	}
294 
295 	_RefsReceived(message);
296 }
297 
298 
299 bool
300 BrowserApp::QuitRequested()
301 {
302 	if (fDownloadWindow->DownloadsInProgress()) {
303 		BAlert* alert = new BAlert(B_TRANSLATE("Downloads in progress"),
304 			B_TRANSLATE("There are still downloads in progress, do you really "
305 			"want to quit WebPositive now?"), B_TRANSLATE("Quit"),
306 			B_TRANSLATE("Continue downloads"));
307 		int32 choice = alert->Go();
308 		if (choice == 1) {
309 			if (fWindowCount == 0) {
310 				if (fDownloadWindow->Lock()) {
311 					fDownloadWindow->SetWorkspaces(1 << current_workspace());
312 					if (fDownloadWindow->IsHidden())
313 						fDownloadWindow->Show();
314 					else
315 						fDownloadWindow->Activate();
316 					fDownloadWindow->SetMinimizeOnClose(true);
317 					fDownloadWindow->Unlock();
318 					return false;
319 				}
320 			} else
321 				return false;
322 		}
323 	}
324 
325 	for (int i = 0; BWindow* window = WindowAt(i); i++) {
326 		BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
327 		if (!webWindow)
328 			continue;
329 		if (!webWindow->Lock())
330 			continue;
331 		if (webWindow->QuitRequested()) {
332 			fLastWindowFrame = webWindow->WindowFrame();
333 			webWindow->Quit();
334 			i--;
335 		} else {
336 			webWindow->Unlock();
337 			return false;
338 		}
339 	}
340 
341 	BWebPage::ShutdownOnce();
342 
343 	fSettings->SetValue("window frame", fLastWindowFrame);
344 	if (fDownloadWindow->Lock()) {
345 		fSettings->SetValue("downloads window frame", fDownloadWindow->Frame());
346 		fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden());
347 		fDownloadWindow->Unlock();
348 	}
349 	if (fSettingsWindow->Lock()) {
350 		fSettings->SetValue("settings window frame", fSettingsWindow->Frame());
351 		fSettingsWindow->Unlock();
352 	}
353 
354 	BMessage cookieArchive;
355 	if (fCookieJar != NULL && fCookieJar->Archive(&cookieArchive) == B_OK)
356 		fCookies->SetValue("cookies", cookieArchive);
357 
358 	return true;
359 }
360 
361 
362 void
363 BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated,
364 	bool* _fullscreen)
365 {
366 	int32 pagesCreated = 0;
367 
368 	bool fullscreen;
369 	if (message->FindBool("fullscreen", &fullscreen) != B_OK)
370 		fullscreen = false;
371 
372 	entry_ref ref;
373 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
374 		BEntry entry(&ref, true);
375 		if (!entry.Exists())
376 			continue;
377 		BPath path;
378 		if (entry.GetPath(&path) != B_OK)
379 			continue;
380 		BString url;
381 		url << path.Path();
382 		_CreateNewPage(url, fullscreen);
383 		pagesCreated++;
384 	}
385 
386 	BString url;
387 	for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) {
388 		_CreateNewPage(url, fullscreen);
389 		pagesCreated++;
390 	}
391 
392 	if (_pagesCreated != NULL)
393 		*_pagesCreated = pagesCreated;
394 	if (_fullscreen != NULL)
395 		*_fullscreen = fullscreen;
396 }
397 
398 
399 void
400 BrowserApp::_CreateNewPage(const BString& url, bool fullscreen)
401 {
402 	uint32 workspace = 1 << current_workspace();
403 
404 	bool loadedInWindowOnCurrentWorkspace = false;
405 	for (int i = 0; BWindow* window = WindowAt(i); i++) {
406 		BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
407 		if (!webWindow)
408 			continue;
409 		if (webWindow->Lock()) {
410 			if (webWindow->Workspaces() & workspace) {
411 				if (webWindow->IsBlankTab()) {
412 					if (url.Length() != 0)
413 						webWindow->CurrentWebView()->LoadURL(url);
414 				} else
415 					webWindow->CreateNewTab(url, true);
416 				webWindow->Activate();
417 				webWindow->CurrentWebView()->MakeFocus(true);
418 				loadedInWindowOnCurrentWorkspace = true;
419 			}
420 			webWindow->Unlock();
421 		}
422 		if (loadedInWindowOnCurrentWorkspace)
423 			return;
424 	}
425 	_CreateNewWindow(url, fullscreen);
426 }
427 
428 
429 void
430 BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen)
431 {
432 	// Offset the window frame unless this is the first window created in the
433 	// session.
434 	if (fWindowCount > 0)
435 		fLastWindowFrame.OffsetBy(20, 20);
436 	if (!BScreen().Frame().Contains(fLastWindowFrame))
437 		fLastWindowFrame.OffsetTo(50, 50);
438 
439 	BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings,
440 		url);
441 	if (fullscreen)
442 		window->ToggleFullscreen();
443 	window->Show();
444 }
445 
446 
447 void
448 BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url,
449 	bool select)
450 {
451 	if (!window->Lock())
452 		return;
453 	window->CreateNewTab(url, select);
454 	window->Unlock();
455 }
456 
457 
458 void
459 BrowserApp::_ShowWindow(const BMessage* message, BWindow* window)
460 {
461 	BAutolock _(window);
462 	uint32 workspaces;
463 	if (message->FindUInt32("workspaces", &workspaces) == B_OK)
464 		window->SetWorkspaces(workspaces);
465 	if (window->IsHidden())
466 		window->Show();
467 	else
468 		window->Activate();
469 }
470 
471 
472 // #pragma mark -
473 
474 
475 int
476 main(int, char**)
477 {
478 	try {
479 		new BrowserApp();
480 		be_app->Run();
481 		delete be_app;
482 	} catch (...) {
483 		debugger("Exception caught.");
484 	}
485 
486 	return 0;
487 }
488 
489