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