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 // If no refs led to a new open page, restore previous session. 235 if (pagesCreated == 0) { 236 BMessage archivedWindow; 237 for (int i = 0; fSession->FindMessage("window", i, &archivedWindow) == B_OK; 238 i++) { 239 BRect frame = archivedWindow.FindRect("window frame"); 240 BString url; 241 archivedWindow.FindString("tab", 0, &url); 242 BrowserWindow* window = new(std::nothrow) BrowserWindow(frame, 243 fSettings, url, fContext); 244 245 if (window != NULL) { 246 window->Show(); 247 pagesCreated++; 248 249 for (int j = 1; archivedWindow.FindString("tab", j, &url) == B_OK; 250 j++) { 251 printf("Create %d:%d\n", i, j); 252 _CreateNewTab(window, url, false); 253 pagesCreated++; 254 } 255 } 256 } 257 } 258 259 // If previous session did not contain any window, create a new empty one. 260 if (pagesCreated == 0) 261 _CreateNewWindow("", fullscreen); 262 263 PostMessage(PRELOAD_BROWSING_HISTORY); 264 } 265 266 267 void 268 BrowserApp::MessageReceived(BMessage* message) 269 { 270 switch (message->what) { 271 case PRELOAD_BROWSING_HISTORY: 272 // Accessing the default instance will load the history from disk. 273 BrowsingHistory::DefaultInstance(); 274 break; 275 case B_SILENT_RELAUNCH: 276 _CreateNewPage(""); 277 break; 278 case NEW_WINDOW: { 279 BString url; 280 if (message->FindString("url", &url) != B_OK) 281 break; 282 _CreateNewWindow(url); 283 break; 284 } 285 case NEW_TAB: { 286 BrowserWindow* window; 287 if (message->FindPointer("window", 288 reinterpret_cast<void**>(&window)) != B_OK) 289 break; 290 BString url; 291 message->FindString("url", &url); 292 bool select = false; 293 message->FindBool("select", &select); 294 _CreateNewTab(window, url, select); 295 break; 296 } 297 case WINDOW_OPENED: 298 fWindowCount++; 299 fDownloadWindow->SetMinimizeOnClose(false); 300 break; 301 case WINDOW_CLOSED: 302 fWindowCount--; 303 message->FindRect("window frame", &fLastWindowFrame); 304 if (fWindowCount <= 0) { 305 BMessage* message = new BMessage(B_QUIT_REQUESTED); 306 message->AddMessage("window", DetachCurrentMessage()); 307 PostMessage(message); 308 } 309 break; 310 311 case SHOW_DOWNLOAD_WINDOW: 312 _ShowWindow(message, fDownloadWindow); 313 break; 314 case SHOW_SETTINGS_WINDOW: 315 _ShowWindow(message, fSettingsWindow); 316 break; 317 case SHOW_CONSOLE_WINDOW: 318 _ShowWindow(message, fConsoleWindow); 319 break; 320 case SHOW_COOKIE_WINDOW: 321 _ShowWindow(message, fCookieWindow); 322 break; 323 case ADD_CONSOLE_MESSAGE: 324 fConsoleWindow->PostMessage(message); 325 break; 326 327 default: 328 BApplication::MessageReceived(message); 329 break; 330 } 331 } 332 333 334 void 335 BrowserApp::RefsReceived(BMessage* message) 336 { 337 if (!fInitialized) { 338 delete fLaunchRefsMessage; 339 fLaunchRefsMessage = new BMessage(*message); 340 return; 341 } 342 343 _RefsReceived(message); 344 } 345 346 347 bool 348 BrowserApp::QuitRequested() 349 { 350 if (fDownloadWindow->DownloadsInProgress()) { 351 BAlert* alert = new BAlert(B_TRANSLATE("Downloads in progress"), 352 B_TRANSLATE("There are still downloads in progress, do you really " 353 "want to quit WebPositive now?"), B_TRANSLATE("Quit"), 354 B_TRANSLATE("Continue downloads")); 355 int32 choice = alert->Go(); 356 if (choice == 1) { 357 if (fWindowCount == 0) { 358 if (fDownloadWindow->Lock()) { 359 fDownloadWindow->SetWorkspaces(1 << current_workspace()); 360 if (fDownloadWindow->IsHidden()) 361 fDownloadWindow->Show(); 362 else 363 fDownloadWindow->Activate(); 364 fDownloadWindow->SetMinimizeOnClose(true); 365 fDownloadWindow->Unlock(); 366 return false; 367 } 368 } else 369 return false; 370 } 371 } 372 373 fSession->MakeEmpty(); 374 375 /* See if we got here because the last window is already closed. 376 * In that case we only need to save that one, which is already archived */ 377 BMessage* message = CurrentMessage(); 378 BMessage windowMessage; 379 380 status_t ret = message->FindMessage("window", &windowMessage); 381 if (ret == B_OK) { 382 fSession->AddMessage("window", &windowMessage); 383 } else { 384 for (int i = 0; BWindow* window = WindowAt(i); i++) { 385 BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window); 386 if (!webWindow) 387 continue; 388 if (!webWindow->Lock()) 389 continue; 390 391 BMessage windowArchive; 392 webWindow->Archive(&windowArchive, true); 393 fSession->AddMessage("window", &windowArchive); 394 395 if (webWindow->QuitRequested()) { 396 fLastWindowFrame = webWindow->WindowFrame(); 397 webWindow->Quit(); 398 i--; 399 } else { 400 webWindow->Unlock(); 401 return false; 402 } 403 } 404 } 405 406 BWebPage::ShutdownOnce(); 407 408 fSettings->SetValue("window frame", fLastWindowFrame); 409 if (fDownloadWindow->Lock()) { 410 fSettings->SetValue("downloads window frame", fDownloadWindow->Frame()); 411 fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden()); 412 fDownloadWindow->Unlock(); 413 } 414 if (fSettingsWindow->Lock()) { 415 fSettings->SetValue("settings window frame", fSettingsWindow->Frame()); 416 fSettingsWindow->Unlock(); 417 } 418 if (fConsoleWindow->Lock()) { 419 fSettings->SetValue("console window frame", fConsoleWindow->Frame()); 420 fConsoleWindow->Unlock(); 421 } 422 if (fCookieWindow->Lock()) { 423 fSettings->SetValue("cookie window frame", fCookieWindow->Frame()); 424 fCookieWindow->Unlock(); 425 } 426 427 BMessage cookieArchive; 428 BNetworkCookieJar& cookieJar = fContext->GetCookieJar(); 429 cookieJar.PurgeForExit(); 430 if (cookieJar.Archive(&cookieArchive) == B_OK) 431 fCookies->SetValue("cookies", cookieArchive); 432 433 return true; 434 } 435 436 437 void 438 BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated, 439 bool* _fullscreen) 440 { 441 int32 pagesCreated = 0; 442 443 BrowserWindow* window = NULL; 444 if (message->FindPointer("window", (void**)&window) != B_OK) 445 window = NULL; 446 447 bool fullscreen; 448 if (message->FindBool("fullscreen", &fullscreen) != B_OK) 449 fullscreen = false; 450 451 entry_ref ref; 452 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 453 BEntry entry(&ref, true); 454 if (!entry.Exists()) 455 continue; 456 BPath path; 457 if (entry.GetPath(&path) != B_OK) 458 continue; 459 BUrl url(path); 460 window = _CreateNewPage(url.UrlString(), window, fullscreen, 461 pagesCreated == 0); 462 pagesCreated++; 463 } 464 465 BString url; 466 for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) { 467 window = _CreateNewPage(url, window, fullscreen, pagesCreated == 0); 468 pagesCreated++; 469 } 470 471 if (_pagesCreated != NULL) 472 *_pagesCreated = pagesCreated; 473 if (_fullscreen != NULL) 474 *_fullscreen = fullscreen; 475 } 476 477 478 BrowserWindow* 479 BrowserApp::_CreateNewPage(const BString& url, BrowserWindow* webWindow, 480 bool fullscreen, bool useBlankTab) 481 { 482 // Let's first see if we must target a specific window... 483 if (webWindow && webWindow->Lock()) { 484 if (useBlankTab && webWindow->IsBlankTab()) { 485 if (url.Length() != 0) 486 webWindow->CurrentWebView()->LoadURL(url); 487 } else 488 webWindow->CreateNewTab(url, true); 489 webWindow->Activate(); 490 webWindow->CurrentWebView()->MakeFocus(true); 491 webWindow->Unlock(); 492 return webWindow; 493 } 494 495 // Otherwise, try to find one in the current workspace 496 uint32 workspace = 1 << current_workspace(); 497 498 bool loadedInWindowOnCurrentWorkspace = false; 499 for (int i = 0; BWindow* window = WindowAt(i); i++) { 500 webWindow = dynamic_cast<BrowserWindow*>(window); 501 if (!webWindow) 502 continue; 503 504 if (webWindow->Lock()) { 505 if (webWindow->Workspaces() & workspace) { 506 if (useBlankTab && webWindow->IsBlankTab()) { 507 if (url.Length() != 0) 508 webWindow->CurrentWebView()->LoadURL(url); 509 } else 510 webWindow->CreateNewTab(url, true); 511 webWindow->Activate(); 512 webWindow->CurrentWebView()->MakeFocus(true); 513 loadedInWindowOnCurrentWorkspace = true; 514 } 515 webWindow->Unlock(); 516 } 517 if (loadedInWindowOnCurrentWorkspace) 518 return webWindow; 519 } 520 521 // Finally, if no window is available, let's create one. 522 return _CreateNewWindow(url, fullscreen); 523 } 524 525 526 BrowserWindow* 527 BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen) 528 { 529 // Offset the window frame unless this is the first window created in the 530 // session. 531 if (fWindowCount > 0) 532 fLastWindowFrame.OffsetBy(20, 20); 533 if (!BScreen().Frame().Contains(fLastWindowFrame)) 534 fLastWindowFrame.OffsetTo(50, 50); 535 536 BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings, 537 url, fContext); 538 if (fullscreen) 539 window->ToggleFullscreen(); 540 window->Show(); 541 return window; 542 } 543 544 545 void 546 BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url, 547 bool select) 548 { 549 if (!window->Lock()) 550 return; 551 window->CreateNewTab(url, select); 552 window->Unlock(); 553 } 554 555 556 void 557 BrowserApp::_ShowWindow(const BMessage* message, BWindow* window) 558 { 559 BAutolock _(window); 560 uint32 workspaces; 561 if (message->FindUInt32("workspaces", &workspaces) == B_OK) 562 window->SetWorkspaces(workspaces); 563 if (window->IsHidden()) 564 window->Show(); 565 else 566 window->Activate(); 567 } 568 569 570 // #pragma mark - 571 572 573 int 574 main(int, char**) 575 { 576 try { 577 new BrowserApp(); 578 be_app->Run(); 579 delete be_app; 580 } catch (...) { 581 debugger("Exception caught."); 582 } 583 584 return 0; 585 } 586 587